1717package io .grpc .xds ;
1818
1919import com .google .common .annotations .VisibleForTesting ;
20+ import com .google .common .collect .ImmutableMap ;
2021import com .google .protobuf .Any ;
21- import com .google .protobuf .InvalidProtocolBufferException ;
22+ import com .google .protobuf .Struct ;
23+ import io .envoyproxy .envoy .config .core .v3 .Metadata ;
2224import io .grpc .xds .GcpAuthenticationFilter .AudienceMetadataParser ;
25+ import io .grpc .xds .XdsEndpointResource .AddressMetadataParser ;
26+ import io .grpc .xds .client .XdsResourceType .ResourceInvalidException ;
27+ import io .grpc .xds .internal .ProtobufJsonConverter ;
2328import java .util .HashMap ;
2429import java .util .Map ;
2530
@@ -36,6 +41,7 @@ final class MetadataRegistry {
3641
3742 private MetadataRegistry () {
3843 registerParser (new AudienceMetadataParser ());
44+ registerParser (new AddressMetadataParser ());
3945 }
4046
4147 static MetadataRegistry getInstance () {
@@ -55,6 +61,54 @@ void removeParser(MetadataValueParser parser) {
5561 supportedParsers .remove (parser .getTypeUrl ());
5662 }
5763
64+ /**
65+ * Parses cluster metadata into a structured map.
66+ *
67+ * <p>Values in {@code typed_filter_metadata} take precedence over
68+ * {@code filter_metadata} when keys overlap, following Envoy API behavior. See
69+ * <a href="https://github.com/envoyproxy/envoy/blob/main/api/envoy/config/core/v3/base.proto#L217-L259">
70+ * Envoy metadata documentation </a> for details.
71+ *
72+ * @param metadata the {@link Metadata} containing the fields to parse.
73+ * @return an immutable map of parsed metadata.
74+ * @throws ResourceInvalidException if parsing {@code typed_filter_metadata} fails.
75+ */
76+ public ImmutableMap <String , Object > parseMetadata (Metadata metadata )
77+ throws ResourceInvalidException {
78+ ImmutableMap .Builder <String , Object > parsedMetadata = ImmutableMap .builder ();
79+
80+ // Process typed_filter_metadata
81+ for (Map .Entry <String , Any > entry : metadata .getTypedFilterMetadataMap ().entrySet ()) {
82+ String key = entry .getKey ();
83+ Any value = entry .getValue ();
84+ MetadataValueParser parser = findParser (value .getTypeUrl ());
85+ if (parser != null ) {
86+ try {
87+ Object parsedValue = parser .parse (value );
88+ parsedMetadata .put (key , parsedValue );
89+ } catch (ResourceInvalidException e ) {
90+ throw new ResourceInvalidException (
91+ String .format ("Failed to parse metadata key: %s, type: %s. Error: %s" ,
92+ key , value .getTypeUrl (), e .getMessage ()), e );
93+ }
94+ }
95+ }
96+ // building once to reuse in the next loop
97+ ImmutableMap <String , Object > intermediateParsedMetadata = parsedMetadata .build ();
98+
99+ // Process filter_metadata for remaining keys
100+ for (Map .Entry <String , Struct > entry : metadata .getFilterMetadataMap ().entrySet ()) {
101+ String key = entry .getKey ();
102+ if (!intermediateParsedMetadata .containsKey (key )) {
103+ Struct structValue = entry .getValue ();
104+ Object jsonValue = ProtobufJsonConverter .convertToJson (structValue );
105+ parsedMetadata .put (key , jsonValue );
106+ }
107+ }
108+
109+ return parsedMetadata .build ();
110+ }
111+
58112 interface MetadataValueParser {
59113
60114 String getTypeUrl ();
@@ -64,8 +118,8 @@ interface MetadataValueParser {
64118 *
65119 * @param any the {@link Any} object to parse.
66120 * @return the parsed metadata value.
67- * @throws InvalidProtocolBufferException if the parsing fails.
121+ * @throws ResourceInvalidException if the parsing fails.
68122 */
69- Object parse (Any any ) throws InvalidProtocolBufferException ;
123+ Object parse (Any any ) throws ResourceInvalidException ;
70124 }
71125}
0 commit comments