|  | @@ -971,6 +971,76 @@ MatchType DomainPatternMatchType(const std::string& domain_pattern) {
 | 
	
		
			
				|  |  |    return INVALID_MATCH;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +grpc_error* RouteActionParse(const envoy_api_v2_route_Route* route,
 | 
	
		
			
				|  |  | +                             XdsApi::RdsUpdate::RdsRoute* rds_route) {
 | 
	
		
			
				|  |  | +  if (!envoy_api_v2_route_Route_has_route(route)) {
 | 
	
		
			
				|  |  | +    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +        "No RouteAction found in route.");
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  const envoy_api_v2_route_RouteAction* route_action =
 | 
	
		
			
				|  |  | +      envoy_api_v2_route_Route_route(route);
 | 
	
		
			
				|  |  | +  // Get the cluster or weighted_clusters in the RouteAction.
 | 
	
		
			
				|  |  | +  if (envoy_api_v2_route_RouteAction_has_cluster(route_action)) {
 | 
	
		
			
				|  |  | +    const upb_strview cluster_name =
 | 
	
		
			
				|  |  | +        envoy_api_v2_route_RouteAction_cluster(route_action);
 | 
	
		
			
				|  |  | +    if (cluster_name.size == 0) {
 | 
	
		
			
				|  |  | +      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +          "RouteAction cluster contains empty cluster name.");
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    rds_route->cluster_name = UpbStringToStdString(cluster_name);
 | 
	
		
			
				|  |  | +  } else if (envoy_api_v2_route_RouteAction_has_weighted_clusters(
 | 
	
		
			
				|  |  | +                 route_action)) {
 | 
	
		
			
				|  |  | +    const envoy_api_v2_route_WeightedCluster* weighted_cluster =
 | 
	
		
			
				|  |  | +        envoy_api_v2_route_RouteAction_weighted_clusters(route_action);
 | 
	
		
			
				|  |  | +    uint32_t total_weight = 100;
 | 
	
		
			
				|  |  | +    const google_protobuf_UInt32Value* weight =
 | 
	
		
			
				|  |  | +        envoy_api_v2_route_WeightedCluster_total_weight(weighted_cluster);
 | 
	
		
			
				|  |  | +    if (weight != nullptr) {
 | 
	
		
			
				|  |  | +      total_weight = google_protobuf_UInt32Value_value(weight);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    size_t clusters_size;
 | 
	
		
			
				|  |  | +    const envoy_api_v2_route_WeightedCluster_ClusterWeight* const* clusters =
 | 
	
		
			
				|  |  | +        envoy_api_v2_route_WeightedCluster_clusters(weighted_cluster,
 | 
	
		
			
				|  |  | +                                                    &clusters_size);
 | 
	
		
			
				|  |  | +    uint32_t sum_of_weights = 0;
 | 
	
		
			
				|  |  | +    for (size_t j = 0; j < clusters_size; ++j) {
 | 
	
		
			
				|  |  | +      const envoy_api_v2_route_WeightedCluster_ClusterWeight* cluster_weight =
 | 
	
		
			
				|  |  | +          clusters[j];
 | 
	
		
			
				|  |  | +      XdsApi::RdsUpdate::RdsRoute::ClusterWeight cluster;
 | 
	
		
			
				|  |  | +      cluster.name = UpbStringToStdString(
 | 
	
		
			
				|  |  | +          envoy_api_v2_route_WeightedCluster_ClusterWeight_name(
 | 
	
		
			
				|  |  | +              cluster_weight));
 | 
	
		
			
				|  |  | +      if (cluster.name.empty()) {
 | 
	
		
			
				|  |  | +        return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +            "RouteAction weighted_cluster cluster contains empty cluster "
 | 
	
		
			
				|  |  | +            "name.");
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      const google_protobuf_UInt32Value* weight =
 | 
	
		
			
				|  |  | +          envoy_api_v2_route_WeightedCluster_ClusterWeight_weight(
 | 
	
		
			
				|  |  | +              cluster_weight);
 | 
	
		
			
				|  |  | +      if (weight == nullptr) {
 | 
	
		
			
				|  |  | +        return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +            "RouteAction weighted_cluster cluster missing weight");
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      cluster.weight = google_protobuf_UInt32Value_value(weight);
 | 
	
		
			
				|  |  | +      sum_of_weights += cluster.weight;
 | 
	
		
			
				|  |  | +      rds_route->weighted_clusters.emplace_back(std::move(cluster));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if (total_weight != sum_of_weights) {
 | 
	
		
			
				|  |  | +      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +          "RouteAction weighted_cluster has incorrect total weight");
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if (rds_route->weighted_clusters.empty()) {
 | 
	
		
			
				|  |  | +      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +          "RouteAction weighted_cluster has no valid clusters specified.");
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +        "No cluster or weighted_clusters found in RouteAction.");
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return GRPC_ERROR_NONE;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  grpc_error* RouteConfigParse(
 | 
	
		
			
				|  |  |      XdsClient* client, TraceFlag* tracer,
 | 
	
		
			
				|  |  |      const envoy_api_v2_RouteConfiguration* route_config,
 | 
	
	
		
			
				|  | @@ -1037,8 +1107,30 @@ grpc_error* RouteConfigParse(
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    // If xds_routing is not configured, only look at the last one in the route
 | 
	
		
			
				|  |  |    // list (the default route)
 | 
	
		
			
				|  |  | -  size_t start_index = xds_routing_enabled ? 0 : size - 1;
 | 
	
		
			
				|  |  | -  for (size_t i = start_index; i < size; ++i) {
 | 
	
		
			
				|  |  | +  if (!xds_routing_enabled) {
 | 
	
		
			
				|  |  | +    const envoy_api_v2_route_Route* route = routes[size - 1];
 | 
	
		
			
				|  |  | +    const envoy_api_v2_route_RouteMatch* match =
 | 
	
		
			
				|  |  | +        envoy_api_v2_route_Route_match(route);
 | 
	
		
			
				|  |  | +    XdsApi::RdsUpdate::RdsRoute rds_route;
 | 
	
		
			
				|  |  | +    // if xds routing is not enabled, we must be working on the default route;
 | 
	
		
			
				|  |  | +    // in this case, we must have an empty or single slash prefix.
 | 
	
		
			
				|  |  | +    if (!envoy_api_v2_route_RouteMatch_has_prefix(match)) {
 | 
	
		
			
				|  |  | +      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +          "No prefix field found in Default RouteMatch.");
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    const upb_strview prefix = envoy_api_v2_route_RouteMatch_prefix(match);
 | 
	
		
			
				|  |  | +    if (!upb_strview_eql(prefix, upb_strview_makez("")) &&
 | 
	
		
			
				|  |  | +        !upb_strview_eql(prefix, upb_strview_makez("/"))) {
 | 
	
		
			
				|  |  | +      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | +          "Default route must have empty prefix.");
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    grpc_error* error = RouteActionParse(route, &rds_route);
 | 
	
		
			
				|  |  | +    if (error != GRPC_ERROR_NONE) return error;
 | 
	
		
			
				|  |  | +    rds_update->routes.emplace_back(std::move(rds_route));
 | 
	
		
			
				|  |  | +    return GRPC_ERROR_NONE;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  // Loop over the whole list of routes
 | 
	
		
			
				|  |  | +  for (size_t i = 0; i < size; ++i) {
 | 
	
		
			
				|  |  |      const envoy_api_v2_route_Route* route = routes[i];
 | 
	
		
			
				|  |  |      const envoy_api_v2_route_RouteMatch* match =
 | 
	
		
			
				|  |  |          envoy_api_v2_route_Route_match(route);
 | 
	
	
		
			
				|  | @@ -1094,85 +1186,16 @@ grpc_error* RouteConfigParse(
 | 
	
		
			
				|  |  |        rds_route.service = std::string(path_elements[0]);
 | 
	
		
			
				|  |  |        rds_route.method = std::string(path_elements[1]);
 | 
	
		
			
				|  |  |      } else {
 | 
	
		
			
				|  |  | -      // TODO(donnadionne): We may change this behavior once we decide how to
 | 
	
		
			
				|  |  | -      // handle unsupported fields.
 | 
	
		
			
				|  |  | +      // Path specifier types will be supported, ignore but not reject until
 | 
	
		
			
				|  |  | +      // they are implemented.
 | 
	
		
			
				|  |  |        continue;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    if (!envoy_api_v2_route_Route_has_route(route)) {
 | 
	
		
			
				|  |  | -      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | -          "No RouteAction found in route.");
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    const envoy_api_v2_route_RouteAction* route_action =
 | 
	
		
			
				|  |  | -        envoy_api_v2_route_Route_route(route);
 | 
	
		
			
				|  |  | -    // Get the cluster or weighted_clusters in the RouteAction.
 | 
	
		
			
				|  |  | -    if (envoy_api_v2_route_RouteAction_has_cluster(route_action)) {
 | 
	
		
			
				|  |  | -      const upb_strview cluster_name =
 | 
	
		
			
				|  |  | -          envoy_api_v2_route_RouteAction_cluster(route_action);
 | 
	
		
			
				|  |  | -      if (cluster_name.size == 0) {
 | 
	
		
			
				|  |  | -        return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | -            "RouteAction cluster contains empty cluster name.");
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      rds_route.cluster_name = UpbStringToStdString(cluster_name);
 | 
	
		
			
				|  |  | -    } else if (envoy_api_v2_route_RouteAction_has_weighted_clusters(
 | 
	
		
			
				|  |  | -                   route_action)) {
 | 
	
		
			
				|  |  | -      const envoy_api_v2_route_WeightedCluster* weighted_cluster =
 | 
	
		
			
				|  |  | -          envoy_api_v2_route_RouteAction_weighted_clusters(route_action);
 | 
	
		
			
				|  |  | -      uint32_t total_weight = 100;
 | 
	
		
			
				|  |  | -      const google_protobuf_UInt32Value* weight =
 | 
	
		
			
				|  |  | -          envoy_api_v2_route_WeightedCluster_total_weight(weighted_cluster);
 | 
	
		
			
				|  |  | -      if (weight != nullptr) {
 | 
	
		
			
				|  |  | -        total_weight = google_protobuf_UInt32Value_value(weight);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      size_t clusters_size;
 | 
	
		
			
				|  |  | -      const envoy_api_v2_route_WeightedCluster_ClusterWeight* const* clusters =
 | 
	
		
			
				|  |  | -          envoy_api_v2_route_WeightedCluster_clusters(weighted_cluster,
 | 
	
		
			
				|  |  | -                                                      &clusters_size);
 | 
	
		
			
				|  |  | -      uint32_t sum_of_weights = 0;
 | 
	
		
			
				|  |  | -      for (size_t j = 0; j < clusters_size; ++j) {
 | 
	
		
			
				|  |  | -        const envoy_api_v2_route_WeightedCluster_ClusterWeight* cluster_weight =
 | 
	
		
			
				|  |  | -            clusters[j];
 | 
	
		
			
				|  |  | -        XdsApi::RdsUpdate::RdsRoute::ClusterWeight cluster;
 | 
	
		
			
				|  |  | -        cluster.name = UpbStringToStdString(
 | 
	
		
			
				|  |  | -            envoy_api_v2_route_WeightedCluster_ClusterWeight_name(
 | 
	
		
			
				|  |  | -                cluster_weight));
 | 
	
		
			
				|  |  | -        if (cluster.name.empty()) {
 | 
	
		
			
				|  |  | -          return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | -              "RouteAction weighted_cluster cluster contains empty cluster "
 | 
	
		
			
				|  |  | -              "name.");
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        const google_protobuf_UInt32Value* weight =
 | 
	
		
			
				|  |  | -            envoy_api_v2_route_WeightedCluster_ClusterWeight_weight(
 | 
	
		
			
				|  |  | -                cluster_weight);
 | 
	
		
			
				|  |  | -        if (weight == nullptr) {
 | 
	
		
			
				|  |  | -          return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | -              "RouteAction weighted_cluster cluster missing weight");
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        cluster.weight = google_protobuf_UInt32Value_value(weight);
 | 
	
		
			
				|  |  | -        sum_of_weights += cluster.weight;
 | 
	
		
			
				|  |  | -        rds_route.weighted_clusters.emplace_back(std::move(cluster));
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      if (total_weight != sum_of_weights) {
 | 
	
		
			
				|  |  | -        return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | -            "RouteAction weighted_cluster has incorrect total weight");
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      if (rds_route.weighted_clusters.empty()) {
 | 
	
		
			
				|  |  | -        return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | -            "RouteAction weighted_cluster has no valid clusters specified.");
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | -          "No cluster or weighted_clusters found in RouteAction.");
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +    grpc_error* error = RouteActionParse(route, &rds_route);
 | 
	
		
			
				|  |  | +    if (error != GRPC_ERROR_NONE) return error;
 | 
	
		
			
				|  |  |      rds_update->routes.emplace_back(std::move(rds_route));
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    if (rds_update->routes.empty()) {
 | 
	
		
			
				|  |  |      return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No valid routes specified.");
 | 
	
		
			
				|  |  | -  } else {
 | 
	
		
			
				|  |  | -    if (!rds_update->routes.back().service.empty() ||
 | 
	
		
			
				|  |  | -        !rds_update->routes.back().method.empty()) {
 | 
	
		
			
				|  |  | -      return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
 | 
	
		
			
				|  |  | -          "Default route must have empty service and method");
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    return GRPC_ERROR_NONE;
 | 
	
		
			
				|  |  |  }
 |