|  | @@ -156,6 +156,83 @@ bool XdsFaultInjectionEnabled() {
 | 
	
		
			
				|  |  |    return parse_succeeded && parsed_value;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +// XdsApi::Route::HashPolicy
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +XdsApi::Route::HashPolicy::HashPolicy(const HashPolicy& other)
 | 
	
		
			
				|  |  | +    : type(other.type),
 | 
	
		
			
				|  |  | +      header_name(other.header_name),
 | 
	
		
			
				|  |  | +      regex_substitution(other.regex_substitution) {
 | 
	
		
			
				|  |  | +  if (other.regex != nullptr) {
 | 
	
		
			
				|  |  | +    regex =
 | 
	
		
			
				|  |  | +        absl::make_unique<RE2>(other.regex->pattern(), other.regex->options());
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +XdsApi::Route::HashPolicy& XdsApi::Route::HashPolicy::operator=(
 | 
	
		
			
				|  |  | +    const HashPolicy& other) {
 | 
	
		
			
				|  |  | +  type = other.type;
 | 
	
		
			
				|  |  | +  header_name = other.header_name;
 | 
	
		
			
				|  |  | +  if (other.regex != nullptr) {
 | 
	
		
			
				|  |  | +    regex =
 | 
	
		
			
				|  |  | +        absl::make_unique<RE2>(other.regex->pattern(), other.regex->options());
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  regex_substitution = other.regex_substitution;
 | 
	
		
			
				|  |  | +  return *this;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +XdsApi::Route::HashPolicy::HashPolicy(HashPolicy&& other) noexcept
 | 
	
		
			
				|  |  | +    : type(other.type),
 | 
	
		
			
				|  |  | +      header_name(std::move(other.header_name)),
 | 
	
		
			
				|  |  | +      regex(std::move(other.regex)),
 | 
	
		
			
				|  |  | +      regex_substitution(std::move(other.regex_substitution)) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +XdsApi::Route::HashPolicy& XdsApi::Route::HashPolicy::operator=(
 | 
	
		
			
				|  |  | +    HashPolicy&& other) noexcept {
 | 
	
		
			
				|  |  | +  type = other.type;
 | 
	
		
			
				|  |  | +  header_name = std::move(other.header_name);
 | 
	
		
			
				|  |  | +  regex = std::move(other.regex);
 | 
	
		
			
				|  |  | +  regex_substitution = std::move(other.regex_substitution);
 | 
	
		
			
				|  |  | +  return *this;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +bool XdsApi::Route::HashPolicy::HashPolicy::operator==(
 | 
	
		
			
				|  |  | +    const HashPolicy& other) const {
 | 
	
		
			
				|  |  | +  if (type != other.type) return false;
 | 
	
		
			
				|  |  | +  if (type == Type::HEADER) {
 | 
	
		
			
				|  |  | +    if (regex == nullptr) {
 | 
	
		
			
				|  |  | +      if (other.regex != nullptr) return false;
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      if (other.regex == nullptr) return false;
 | 
	
		
			
				|  |  | +      return header_name == other.header_name &&
 | 
	
		
			
				|  |  | +             regex->pattern() == other.regex->pattern() &&
 | 
	
		
			
				|  |  | +             regex_substitution == other.regex_substitution;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return true;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +std::string XdsApi::Route::HashPolicy::ToString() const {
 | 
	
		
			
				|  |  | +  std::vector<std::string> contents;
 | 
	
		
			
				|  |  | +  switch (type) {
 | 
	
		
			
				|  |  | +    case Type::HEADER:
 | 
	
		
			
				|  |  | +      contents.push_back("type=HEADER");
 | 
	
		
			
				|  |  | +      break;
 | 
	
		
			
				|  |  | +    case Type::CHANNEL_ID:
 | 
	
		
			
				|  |  | +      contents.push_back("type=CHANNEL_ID");
 | 
	
		
			
				|  |  | +      break;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  contents.push_back(
 | 
	
		
			
				|  |  | +      absl::StrFormat("terminal=%s", terminal ? "true" : "false"));
 | 
	
		
			
				|  |  | +  if (type == Type::HEADER) {
 | 
	
		
			
				|  |  | +    contents.push_back(absl::StrFormat(
 | 
	
		
			
				|  |  | +        "Header %s:/%s/%s", header_name,
 | 
	
		
			
				|  |  | +        (regex == nullptr) ? "" : regex->pattern(), regex_substitution));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  //
 | 
	
		
			
				|  |  |  // XdsApi::Route
 | 
	
		
			
				|  |  |  //
 | 
	
	
		
			
				|  | @@ -194,6 +271,9 @@ std::string XdsApi::Route::ClusterWeight::ToString() const {
 | 
	
		
			
				|  |  |  std::string XdsApi::Route::ToString() const {
 | 
	
		
			
				|  |  |    std::vector<std::string> contents;
 | 
	
		
			
				|  |  |    contents.push_back(matchers.ToString());
 | 
	
		
			
				|  |  | +  for (const HashPolicy& hash_policy : hash_policies) {
 | 
	
		
			
				|  |  | +    contents.push_back(absl::StrCat("hash_policy=", hash_policy.ToString()));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |    if (!cluster_name.empty()) {
 | 
	
		
			
				|  |  |      contents.push_back(absl::StrFormat("Cluster name: %s", cluster_name));
 | 
	
		
			
				|  |  |    }
 | 
	
	
		
			
				|  | @@ -1468,6 +1548,88 @@ grpc_error* RouteActionParse(const EncodingContext& context,
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +  // Get HashPolicy from RouteAction
 | 
	
		
			
				|  |  | +  if (XdsRingHashEnabled()) {
 | 
	
		
			
				|  |  | +    size_t size = 0;
 | 
	
		
			
				|  |  | +    const envoy_config_route_v3_RouteAction_HashPolicy* const* hash_policies =
 | 
	
		
			
				|  |  | +        envoy_config_route_v3_RouteAction_hash_policy(route_action, &size);
 | 
	
		
			
				|  |  | +    for (size_t i = 0; i < size; ++i) {
 | 
	
		
			
				|  |  | +      const envoy_config_route_v3_RouteAction_HashPolicy* hash_policy =
 | 
	
		
			
				|  |  | +          hash_policies[i];
 | 
	
		
			
				|  |  | +      XdsApi::Route::HashPolicy policy;
 | 
	
		
			
				|  |  | +      policy.terminal =
 | 
	
		
			
				|  |  | +          envoy_config_route_v3_RouteAction_HashPolicy_terminal(hash_policy);
 | 
	
		
			
				|  |  | +      const envoy_config_route_v3_RouteAction_HashPolicy_Header* header;
 | 
	
		
			
				|  |  | +      const envoy_config_route_v3_RouteAction_HashPolicy_FilterState*
 | 
	
		
			
				|  |  | +          filter_state;
 | 
	
		
			
				|  |  | +      if ((header = envoy_config_route_v3_RouteAction_HashPolicy_header(
 | 
	
		
			
				|  |  | +               hash_policy)) != nullptr) {
 | 
	
		
			
				|  |  | +        policy.type = XdsApi::Route::HashPolicy::Type::HEADER;
 | 
	
		
			
				|  |  | +        policy.header_name = UpbStringToStdString(
 | 
	
		
			
				|  |  | +            envoy_config_route_v3_RouteAction_HashPolicy_Header_header_name(
 | 
	
		
			
				|  |  | +                header));
 | 
	
		
			
				|  |  | +        const struct envoy_type_matcher_v3_RegexMatchAndSubstitute*
 | 
	
		
			
				|  |  | +            regex_rewrite =
 | 
	
		
			
				|  |  | +                envoy_config_route_v3_RouteAction_HashPolicy_Header_regex_rewrite(
 | 
	
		
			
				|  |  | +                    header);
 | 
	
		
			
				|  |  | +        if (regex_rewrite == nullptr) {
 | 
	
		
			
				|  |  | +          gpr_log(
 | 
	
		
			
				|  |  | +              GPR_DEBUG,
 | 
	
		
			
				|  |  | +              "RouteAction HashPolicy contains policy specifier Header with "
 | 
	
		
			
				|  |  | +              "RegexMatchAndSubstitution but Regex is missing");
 | 
	
		
			
				|  |  | +          continue;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        const envoy_type_matcher_v3_RegexMatcher* regex_matcher =
 | 
	
		
			
				|  |  | +            envoy_type_matcher_v3_RegexMatchAndSubstitute_pattern(
 | 
	
		
			
				|  |  | +                regex_rewrite);
 | 
	
		
			
				|  |  | +        if (regex_matcher == nullptr) {
 | 
	
		
			
				|  |  | +          gpr_log(
 | 
	
		
			
				|  |  | +              GPR_DEBUG,
 | 
	
		
			
				|  |  | +              "RouteAction HashPolicy contains policy specifier Header with "
 | 
	
		
			
				|  |  | +              "RegexMatchAndSubstitution but RegexMatcher pattern is "
 | 
	
		
			
				|  |  | +              "missing");
 | 
	
		
			
				|  |  | +          continue;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        RE2::Options options;
 | 
	
		
			
				|  |  | +        policy.regex = absl::make_unique<RE2>(
 | 
	
		
			
				|  |  | +            UpbStringToStdString(
 | 
	
		
			
				|  |  | +                envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher)),
 | 
	
		
			
				|  |  | +            options);
 | 
	
		
			
				|  |  | +        if (!policy.regex->ok()) {
 | 
	
		
			
				|  |  | +          gpr_log(
 | 
	
		
			
				|  |  | +              GPR_DEBUG,
 | 
	
		
			
				|  |  | +              "RouteAction HashPolicy contains policy specifier Header with "
 | 
	
		
			
				|  |  | +              "RegexMatchAndSubstitution but RegexMatcher pattern does not "
 | 
	
		
			
				|  |  | +              "compile");
 | 
	
		
			
				|  |  | +          continue;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        policy.regex_substitution = UpbStringToStdString(
 | 
	
		
			
				|  |  | +            envoy_type_matcher_v3_RegexMatchAndSubstitute_substitution(
 | 
	
		
			
				|  |  | +                regex_rewrite));
 | 
	
		
			
				|  |  | +      } else if ((filter_state =
 | 
	
		
			
				|  |  | +                      envoy_config_route_v3_RouteAction_HashPolicy_filter_state(
 | 
	
		
			
				|  |  | +                          hash_policy)) != nullptr) {
 | 
	
		
			
				|  |  | +        std::string key = UpbStringToStdString(
 | 
	
		
			
				|  |  | +            envoy_config_route_v3_RouteAction_HashPolicy_FilterState_key(
 | 
	
		
			
				|  |  | +                filter_state));
 | 
	
		
			
				|  |  | +        if (key == "io.grpc.channel_id") {
 | 
	
		
			
				|  |  | +          policy.type = XdsApi::Route::HashPolicy::Type::CHANNEL_ID;
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +          gpr_log(GPR_DEBUG,
 | 
	
		
			
				|  |  | +                  "RouteAction HashPolicy contains policy specifier "
 | 
	
		
			
				|  |  | +                  "FilterState but "
 | 
	
		
			
				|  |  | +                  "key is not io.grpc.channel_id.");
 | 
	
		
			
				|  |  | +          continue;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        gpr_log(
 | 
	
		
			
				|  |  | +            GPR_DEBUG,
 | 
	
		
			
				|  |  | +            "RouteAction HashPolicy contains unsupported policy specifier.");
 | 
	
		
			
				|  |  | +        continue;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      route->hash_policies.emplace_back(std::move(policy));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |    return GRPC_ERROR_NONE;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 |