blob: 44338cdf490f67127624159eb0187479b79f357f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.rpc.cluster.router.xds;
import org.apache.dubbo.common.constants.LoggerCodeConstants;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.rpc.cluster.router.xds.rule.ClusterWeight;
import org.apache.dubbo.rpc.cluster.router.xds.rule.HTTPRouteDestination;
import org.apache.dubbo.rpc.cluster.router.xds.rule.HeaderMatcher;
import org.apache.dubbo.rpc.cluster.router.xds.rule.HttpRequestMatch;
import org.apache.dubbo.rpc.cluster.router.xds.rule.LongRangeMatch;
import org.apache.dubbo.rpc.cluster.router.xds.rule.PathMatcher;
import org.apache.dubbo.rpc.cluster.router.xds.rule.XdsRouteRule;
import io.envoyproxy.envoy.config.route.v3.Route;
import io.envoyproxy.envoy.config.route.v3.RouteAction;
import io.envoyproxy.envoy.config.route.v3.RouteMatch;
import io.envoyproxy.envoy.config.route.v3.VirtualHost;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class RdsVirtualHostListener {
private static final ErrorTypeAwareLogger LOGGER =
LoggerFactory.getErrorTypeAwareLogger(RdsVirtualHostListener.class);
private final String domain;
private final RdsRouteRuleManager routeRuleManager;
public RdsVirtualHostListener(String domain, RdsRouteRuleManager routeRuleManager) {
this.domain = domain;
this.routeRuleManager = routeRuleManager;
}
public void parseVirtualHost(VirtualHost virtualHost) {
if (virtualHost == null || CollectionUtils.isEmpty(virtualHost.getRoutesList())) {
// post empty
routeRuleManager.notifyRuleChange(domain, new ArrayList<>());
return;
}
try {
List<XdsRouteRule> xdsRouteRules = virtualHost.getRoutesList().stream()
.map(route -> {
if (route.getMatch().getQueryParametersCount() != 0) {
return null;
}
HttpRequestMatch match = parseMatch(route.getMatch());
HTTPRouteDestination action = parseAction(route);
return new XdsRouteRule(match, action);
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
// post rules
routeRuleManager.notifyRuleChange(domain, xdsRouteRules);
} catch (Exception e) {
LOGGER.error(
LoggerCodeConstants.INTERNAL_ERROR,
"",
"",
"parse domain: " + domain + " xds VirtualHost error",
e);
}
}
private HttpRequestMatch parseMatch(RouteMatch match) {
PathMatcher pathMatcher = parsePathMatch(match);
List<HeaderMatcher> headerMatchers = parseHeadMatch(match);
return new HttpRequestMatch(pathMatcher, headerMatchers);
}
private PathMatcher parsePathMatch(RouteMatch match) {
boolean caseSensitive = match.getCaseSensitive().getValue();
PathMatcher pathMatcher = new PathMatcher();
pathMatcher.setCaseSensitive(caseSensitive);
switch (match.getPathSpecifierCase()) {
case PREFIX:
pathMatcher.setPrefix(match.getPrefix());
return pathMatcher;
case PATH:
pathMatcher.setPath(match.getPath());
return pathMatcher;
case SAFE_REGEX:
String regex = match.getSafeRegex().getRegex();
pathMatcher.setRegex(regex);
return pathMatcher;
case PATHSPECIFIER_NOT_SET:
return null;
default:
throw new IllegalArgumentException("Path specifier is not expect");
}
}
private List<HeaderMatcher> parseHeadMatch(RouteMatch routeMatch) {
List<HeaderMatcher> headerMatchers = new ArrayList<>();
List<io.envoyproxy.envoy.config.route.v3.HeaderMatcher> headersList = routeMatch.getHeadersList();
for (io.envoyproxy.envoy.config.route.v3.HeaderMatcher headerMatcher : headersList) {
HeaderMatcher matcher = new HeaderMatcher();
matcher.setName(headerMatcher.getName());
matcher.setInverted(headerMatcher.getInvertMatch());
switch (headerMatcher.getHeaderMatchSpecifierCase()) {
case EXACT_MATCH:
matcher.setExactValue(headerMatcher.getExactMatch());
headerMatchers.add(matcher);
break;
case SAFE_REGEX_MATCH:
matcher.setRegex(headerMatcher.getSafeRegexMatch().getRegex());
headerMatchers.add(matcher);
break;
case RANGE_MATCH:
LongRangeMatch rang = new LongRangeMatch();
rang.setStart(headerMatcher.getRangeMatch().getStart());
rang.setEnd(headerMatcher.getRangeMatch().getEnd());
matcher.setRange(rang);
headerMatchers.add(matcher);
break;
case PRESENT_MATCH:
matcher.setPresent(headerMatcher.getPresentMatch());
headerMatchers.add(matcher);
break;
case PREFIX_MATCH:
matcher.setPrefix(headerMatcher.getPrefixMatch());
headerMatchers.add(matcher);
break;
case SUFFIX_MATCH:
matcher.setSuffix(headerMatcher.getSuffixMatch());
headerMatchers.add(matcher);
break;
case HEADERMATCHSPECIFIER_NOT_SET:
default:
throw new IllegalArgumentException("Header specifier is not expect");
}
}
return headerMatchers;
}
private HTTPRouteDestination parseAction(Route route) {
switch (route.getActionCase()) {
case ROUTE:
HTTPRouteDestination httpRouteDestination = new HTTPRouteDestination();
// only support cluster and weight cluster
RouteAction routeAction = route.getRoute();
RouteAction.ClusterSpecifierCase clusterSpecifierCase = routeAction.getClusterSpecifierCase();
if (clusterSpecifierCase == RouteAction.ClusterSpecifierCase.CLUSTER) {
httpRouteDestination.setCluster(routeAction.getCluster());
return httpRouteDestination;
} else if (clusterSpecifierCase == RouteAction.ClusterSpecifierCase.WEIGHTED_CLUSTERS) {
List<ClusterWeight> clusterWeights = routeAction.getWeightedClusters().getClustersList().stream()
.map(c ->
new ClusterWeight(c.getName(), c.getWeight().getValue()))
.sorted(Comparator.comparing(ClusterWeight::getWeight))
.collect(Collectors.toList());
httpRouteDestination.setWeightedClusters(clusterWeights);
return httpRouteDestination;
}
case REDIRECT:
case DIRECT_RESPONSE:
case FILTER_ACTION:
case ACTION_NOT_SET:
default:
throw new IllegalArgumentException("Cluster specifier is not expect");
}
}
}