1919import com .google .auto .value .AutoValue ;
2020import com .google .bigtable .v2 .PingAndWarmRequest ;
2121import com .google .bigtable .v2 .PingAndWarmRequest .Builder ;
22+ import com .google .common .annotations .VisibleForTesting ;
2223import com .google .common .collect .ImmutableMap ;
2324import io .grpc .Metadata ;
2425import io .grpc .Metadata .Key ;
2930import java .util .Map ;
3031import java .util .Map .Entry ;
3132import java .util .Optional ;
33+ import org .slf4j .Logger ;
34+ import org .slf4j .LoggerFactory ;
3235
3336/**
3437 * A value class to encapsulate call identity.
4346 */
4447@ AutoValue
4548public abstract class CallLabels {
46- public static final Key <String > REQUEST_PARAMS =
49+ private static final Logger LOG = LoggerFactory .getLogger (CallLabels .class );
50+
51+ // All RLS headers
52+ static final Key <String > REQUEST_PARAMS =
4753 Key .of ("x-goog-request-params" , Metadata .ASCII_STRING_MARSHALLER );
48- private static final Key <String > API_CLIENT =
54+ static final Key <String > LEGACY_RESOURCE_PREFIX =
55+ Key .of ("google-cloud-resource-prefix" , Metadata .ASCII_STRING_MARSHALLER );
56+ static final Key <String > ROUTING_COOKIE =
57+ Key .of ("x-goog-cbt-cookie-routing" , Metadata .ASCII_STRING_MARSHALLER );
58+ static final Key <String > FEATURE_FLAGS =
59+ Key .of ("bigtable-features" , Metadata .ASCII_STRING_MARSHALLER );
60+ static final Key <String > API_CLIENT =
4961 Key .of ("x-goog-api-client" , Metadata .ASCII_STRING_MARSHALLER );
5062
5163 enum ResourceNameType {
@@ -74,36 +86,56 @@ static ResourceName create(ResourceNameType type, String value) {
7486 }
7587 }
7688
77- public abstract Optional < String > getApiClient ();
89+ public abstract String getMethodName ();
7890
79- public abstract Optional <String > getResourceName ();
91+ abstract Optional <String > getRequestParams ();
8092
81- public abstract Optional <String > getAppProfileId ();
93+ abstract Optional <String > getLegacyResourcePrefix ();
8294
83- public abstract String getMethodName ();
95+ abstract Optional <String > getRoutingCookie ();
96+
97+ abstract Optional <String > getEncodedFeatures ();
98+
99+ public abstract Optional <String > getApiClient ();
84100
85101 public static CallLabels create (MethodDescriptor <?, ?> method , Metadata headers ) {
86102 Optional <String > apiClient = Optional .ofNullable (headers .get (API_CLIENT ));
87103
88- String requestParams = Optional .ofNullable (headers .get (REQUEST_PARAMS )).orElse ("" );
89- String [] encodedKvPairs = requestParams .split ("&" );
90- Optional <String > resourceName = extractResourceName (encodedKvPairs ).map (ResourceName ::getValue );
91- Optional <String > appProfile = extractAppProfileId (encodedKvPairs );
104+ Optional <String > requestParams = Optional .ofNullable (headers .get (REQUEST_PARAMS ));
105+ Optional <String > legacyResourcePrefix =
106+ Optional .ofNullable (headers .get (LEGACY_RESOURCE_PREFIX ));
107+ Optional <String > routingCookie = Optional .ofNullable (headers .get (ROUTING_COOKIE ));
108+ Optional <String > encodedFeatures = Optional .ofNullable (headers .get (FEATURE_FLAGS ));
92109
93- return create (method , apiClient , resourceName , appProfile );
110+ return create (
111+ method , requestParams , legacyResourcePrefix , routingCookie , encodedFeatures , apiClient );
94112 }
95113
114+ @ VisibleForTesting
96115 public static CallLabels create (
97116 MethodDescriptor <?, ?> method ,
98- Optional <String > apiClient ,
99- Optional <String > resourceName ,
100- Optional <String > appProfile ) {
117+ Optional <String > requestParams ,
118+ Optional <String > legacyResourcePrefix ,
119+ Optional <String > routingCookie ,
120+ Optional <String > encodedFeatures ,
121+ Optional <String > apiClient ) {
101122
102123 return new AutoValue_CallLabels (
103- apiClient , resourceName , appProfile , method .getFullMethodName ());
124+ method .getFullMethodName (),
125+ requestParams ,
126+ legacyResourcePrefix ,
127+ routingCookie ,
128+ encodedFeatures ,
129+ apiClient );
104130 }
105131
106- private static Optional <ResourceName > extractResourceName (String [] encodedKvPairs ) {
132+ public Optional <String > extractResourceName () throws ParsingException {
133+ if (getRequestParams ().isEmpty ()) {
134+ return getLegacyResourcePrefix ();
135+ }
136+
137+ String requestParams = getRequestParams ().orElse ("" );
138+ String [] encodedKvPairs = requestParams .split ("&" );
107139 Optional <ResourceName > resourceName = Optional .empty ();
108140
109141 for (String encodedKv : encodedKvPairs ) {
@@ -132,10 +164,10 @@ private static Optional<ResourceName> extractResourceName(String[] encodedKvPair
132164
133165 resourceName = Optional .of (ResourceName .create (newType .get (), decodedValue ));
134166 }
135- return resourceName ;
167+ return resourceName . map ( ResourceName :: getValue ) ;
136168 }
137169
138- private static Optional <ResourceNameType > findType (String encodedKey ) {
170+ private static Optional <ResourceNameType > findType (String encodedKey ) throws ParsingException {
139171 String decodedKey = percentDecode (encodedKey );
140172
141173 for (ResourceNameType type : ResourceNameType .values ()) {
@@ -146,8 +178,10 @@ private static Optional<ResourceNameType> findType(String encodedKey) {
146178 return Optional .empty ();
147179 }
148180
149- private static Optional <String > extractAppProfileId (String [] encodedKvPairs ) {
150- for (String encodedPair : encodedKvPairs ) {
181+ public Optional <String > extractAppProfileId () throws ParsingException {
182+ String requestParams = getRequestParams ().orElse ("" );
183+
184+ for (String encodedPair : requestParams .split ("&" )) {
151185 if (!encodedPair .startsWith ("app_profile_id=" )) {
152186 continue ;
153187 }
@@ -158,8 +192,12 @@ private static Optional<String> extractAppProfileId(String[] encodedKvPairs) {
158192 return Optional .empty ();
159193 }
160194
161- private static String percentDecode (String s ) {
162- return URLDecoder .decode (s , StandardCharsets .UTF_8 );
195+ private static String percentDecode (String s ) throws ParsingException {
196+ try {
197+ return URLDecoder .decode (s , StandardCharsets .UTF_8 );
198+ } catch (RuntimeException e ) {
199+ throw new ParsingException ("Failed to url decode " + s , e );
200+ }
163201 }
164202
165203 @ AutoValue
@@ -171,7 +209,9 @@ public abstract static class PrimingKey {
171209 abstract Optional <String > getAppProfileId ();
172210
173211 public static Optional <PrimingKey > from (CallLabels labels ) throws ParsingException {
174- Optional <String > resourceName = labels .getResourceName ();
212+ final ImmutableMap .Builder <String , String > md = ImmutableMap .builder ();
213+
214+ Optional <String > resourceName = labels .extractResourceName ();
175215 if (resourceName .isEmpty ()) {
176216 return Optional .empty ();
177217 }
@@ -188,12 +228,18 @@ public static Optional<PrimingKey> from(CallLabels labels) throws ParsingExcepti
188228 .append ("name=" )
189229 .append (URLEncoder .encode (instanceName , StandardCharsets .UTF_8 ));
190230
191- Optional <String > appProfileId = labels .getAppProfileId ();
231+ Optional <String > appProfileId = labels .extractAppProfileId ();
192232 appProfileId .ifPresent (val -> reqParams .append ("&app_profile_id=" ).append (val ));
193-
194- ImmutableMap .Builder <String , String > md = ImmutableMap .builder ();
195233 md .put (REQUEST_PARAMS .name (), reqParams .toString ());
196234
235+ labels
236+ .getLegacyResourcePrefix ()
237+ .ifPresent (ignored -> md .put (LEGACY_RESOURCE_PREFIX .name (), instanceName ));
238+
239+ labels .getRoutingCookie ().ifPresent (c -> md .put (ROUTING_COOKIE .name (), c ));
240+
241+ labels .getEncodedFeatures ().ifPresent (c -> md .put (FEATURE_FLAGS .name (), c ));
242+
197243 labels .getApiClient ().ifPresent (c -> md .put (API_CLIENT .name (), c ));
198244
199245 return Optional .of (
0 commit comments