1 /* 2 * Copyright 2021 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc.xds; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static com.google.common.base.Preconditions.checkNotNull; 21 22 import com.google.auto.value.AutoValue; 23 import com.google.common.annotations.VisibleForTesting; 24 import com.google.common.collect.ImmutableList; 25 import com.google.common.collect.ImmutableMap; 26 import com.google.protobuf.Duration; 27 import com.google.re2j.Pattern; 28 import io.grpc.Status.Code; 29 import io.grpc.xds.ClusterSpecifierPlugin.NamedPluginConfig; 30 import io.grpc.xds.Filter.FilterConfig; 31 import io.grpc.xds.internal.Matchers.FractionMatcher; 32 import io.grpc.xds.internal.Matchers.HeaderMatcher; 33 import java.util.Collections; 34 import java.util.List; 35 import java.util.Map; 36 import javax.annotation.Nullable; 37 38 /** Represents an upstream virtual host. */ 39 @AutoValue 40 abstract class VirtualHost { 41 // The canonical name of this virtual host. name()42 abstract String name(); 43 44 // The list of domains (host/authority header) that will be matched to this virtual host. domains()45 abstract ImmutableList<String> domains(); 46 47 // The list of routes that will be matched, in order, for incoming requests. routes()48 abstract ImmutableList<Route> routes(); 49 filterConfigOverrides()50 abstract ImmutableMap<String, FilterConfig> filterConfigOverrides(); 51 create( String name, List<String> domains, List<Route> routes, Map<String, FilterConfig> filterConfigOverrides)52 public static VirtualHost create( 53 String name, List<String> domains, List<Route> routes, 54 Map<String, FilterConfig> filterConfigOverrides) { 55 return new AutoValue_VirtualHost(name, ImmutableList.copyOf(domains), 56 ImmutableList.copyOf(routes), ImmutableMap.copyOf(filterConfigOverrides)); 57 } 58 59 @AutoValue 60 abstract static class Route { routeMatch()61 abstract RouteMatch routeMatch(); 62 63 @Nullable routeAction()64 abstract RouteAction routeAction(); 65 filterConfigOverrides()66 abstract ImmutableMap<String, FilterConfig> filterConfigOverrides(); 67 forAction(RouteMatch routeMatch, RouteAction routeAction, Map<String, FilterConfig> filterConfigOverrides)68 static Route forAction(RouteMatch routeMatch, RouteAction routeAction, 69 Map<String, FilterConfig> filterConfigOverrides) { 70 return create(routeMatch, routeAction, filterConfigOverrides); 71 } 72 forNonForwardingAction(RouteMatch routeMatch, Map<String, FilterConfig> filterConfigOverrides)73 static Route forNonForwardingAction(RouteMatch routeMatch, 74 Map<String, FilterConfig> filterConfigOverrides) { 75 return create(routeMatch, null, filterConfigOverrides); 76 } 77 create( RouteMatch routeMatch, @Nullable RouteAction routeAction, Map<String, FilterConfig> filterConfigOverrides)78 private static Route create( 79 RouteMatch routeMatch, @Nullable RouteAction routeAction, 80 Map<String, FilterConfig> filterConfigOverrides) { 81 return new AutoValue_VirtualHost_Route( 82 routeMatch, routeAction, ImmutableMap.copyOf(filterConfigOverrides)); 83 } 84 85 @AutoValue 86 abstract static class RouteMatch { pathMatcher()87 abstract PathMatcher pathMatcher(); 88 headerMatchers()89 abstract ImmutableList<HeaderMatcher> headerMatchers(); 90 91 @Nullable fractionMatcher()92 abstract FractionMatcher fractionMatcher(); 93 94 // TODO(chengyuanzhang): maybe delete me. 95 @VisibleForTesting withPathExactOnly(String path)96 static RouteMatch withPathExactOnly(String path) { 97 return RouteMatch.create(PathMatcher.fromPath(path, true), 98 Collections.<HeaderMatcher>emptyList(), null); 99 } 100 create(PathMatcher pathMatcher, List<HeaderMatcher> headerMatchers, @Nullable FractionMatcher fractionMatcher)101 static RouteMatch create(PathMatcher pathMatcher, 102 List<HeaderMatcher> headerMatchers, @Nullable FractionMatcher fractionMatcher) { 103 return new AutoValue_VirtualHost_Route_RouteMatch(pathMatcher, 104 ImmutableList.copyOf(headerMatchers), fractionMatcher); 105 } 106 107 /** Matcher for HTTP request path. */ 108 @AutoValue 109 abstract static class PathMatcher { 110 // Exact full path to be matched. 111 @Nullable path()112 abstract String path(); 113 114 // Path prefix to be matched. 115 @Nullable prefix()116 abstract String prefix(); 117 118 // Regular expression pattern of the path to be matched. 119 @Nullable regEx()120 abstract Pattern regEx(); 121 122 // Whether case sensitivity is taken into account for matching. 123 // Only valid for full path matching or prefix matching. caseSensitive()124 abstract boolean caseSensitive(); 125 fromPath(String path, boolean caseSensitive)126 static PathMatcher fromPath(String path, boolean caseSensitive) { 127 checkNotNull(path, "path"); 128 return create(path, null, null, caseSensitive); 129 } 130 fromPrefix(String prefix, boolean caseSensitive)131 static PathMatcher fromPrefix(String prefix, boolean caseSensitive) { 132 checkNotNull(prefix, "prefix"); 133 return create(null, prefix, null, caseSensitive); 134 } 135 fromRegEx(Pattern regEx)136 static PathMatcher fromRegEx(Pattern regEx) { 137 checkNotNull(regEx, "regEx"); 138 return create(null, null, regEx, false /* doesn't matter */); 139 } 140 create(@ullable String path, @Nullable String prefix, @Nullable Pattern regEx, boolean caseSensitive)141 private static PathMatcher create(@Nullable String path, @Nullable String prefix, 142 @Nullable Pattern regEx, boolean caseSensitive) { 143 return new AutoValue_VirtualHost_Route_RouteMatch_PathMatcher(path, prefix, regEx, 144 caseSensitive); 145 } 146 } 147 } 148 149 @AutoValue 150 abstract static class RouteAction { 151 // List of hash policies to use for ring hash load balancing. hashPolicies()152 abstract ImmutableList<HashPolicy> hashPolicies(); 153 154 @Nullable timeoutNano()155 abstract Long timeoutNano(); 156 157 @Nullable cluster()158 abstract String cluster(); 159 160 @Nullable weightedClusters()161 abstract ImmutableList<ClusterWeight> weightedClusters(); 162 163 @Nullable namedClusterSpecifierPluginConfig()164 abstract NamedPluginConfig namedClusterSpecifierPluginConfig(); 165 166 @Nullable retryPolicy()167 abstract RetryPolicy retryPolicy(); 168 forCluster( String cluster, List<HashPolicy> hashPolicies, @Nullable Long timeoutNano, @Nullable RetryPolicy retryPolicy)169 static RouteAction forCluster( 170 String cluster, List<HashPolicy> hashPolicies, @Nullable Long timeoutNano, 171 @Nullable RetryPolicy retryPolicy) { 172 checkNotNull(cluster, "cluster"); 173 return RouteAction.create(hashPolicies, timeoutNano, cluster, null, null, retryPolicy); 174 } 175 forWeightedClusters( List<ClusterWeight> weightedClusters, List<HashPolicy> hashPolicies, @Nullable Long timeoutNano, @Nullable RetryPolicy retryPolicy)176 static RouteAction forWeightedClusters( 177 List<ClusterWeight> weightedClusters, List<HashPolicy> hashPolicies, 178 @Nullable Long timeoutNano, @Nullable RetryPolicy retryPolicy) { 179 checkNotNull(weightedClusters, "weightedClusters"); 180 checkArgument(!weightedClusters.isEmpty(), "empty cluster list"); 181 return RouteAction.create( 182 hashPolicies, timeoutNano, null, weightedClusters, null, retryPolicy); 183 } 184 forClusterSpecifierPlugin( NamedPluginConfig namedConfig, List<HashPolicy> hashPolicies, @Nullable Long timeoutNano, @Nullable RetryPolicy retryPolicy)185 static RouteAction forClusterSpecifierPlugin( 186 NamedPluginConfig namedConfig, 187 List<HashPolicy> hashPolicies, 188 @Nullable Long timeoutNano, 189 @Nullable RetryPolicy retryPolicy) { 190 checkNotNull(namedConfig, "namedConfig"); 191 return RouteAction.create(hashPolicies, timeoutNano, null, null, namedConfig, retryPolicy); 192 } 193 create( List<HashPolicy> hashPolicies, @Nullable Long timeoutNano, @Nullable String cluster, @Nullable List<ClusterWeight> weightedClusters, @Nullable NamedPluginConfig namedConfig, @Nullable RetryPolicy retryPolicy)194 private static RouteAction create( 195 List<HashPolicy> hashPolicies, 196 @Nullable Long timeoutNano, 197 @Nullable String cluster, 198 @Nullable List<ClusterWeight> weightedClusters, 199 @Nullable NamedPluginConfig namedConfig, 200 @Nullable RetryPolicy retryPolicy) { 201 return new AutoValue_VirtualHost_Route_RouteAction( 202 ImmutableList.copyOf(hashPolicies), 203 timeoutNano, 204 cluster, 205 weightedClusters == null ? null : ImmutableList.copyOf(weightedClusters), 206 namedConfig, 207 retryPolicy); 208 } 209 210 @AutoValue 211 abstract static class ClusterWeight { name()212 abstract String name(); 213 weight()214 abstract int weight(); 215 filterConfigOverrides()216 abstract ImmutableMap<String, FilterConfig> filterConfigOverrides(); 217 create( String name, int weight, Map<String, FilterConfig> filterConfigOverrides)218 static ClusterWeight create( 219 String name, int weight, Map<String, FilterConfig> filterConfigOverrides) { 220 return new AutoValue_VirtualHost_Route_RouteAction_ClusterWeight( 221 name, weight, ImmutableMap.copyOf(filterConfigOverrides)); 222 } 223 } 224 225 // Configuration for the route's hashing policy if the upstream cluster uses a hashing load 226 // balancer. 227 @AutoValue 228 abstract static class HashPolicy { 229 // The specifier that indicates the component of the request to be hashed on. type()230 abstract Type type(); 231 232 // The flag that short-circuits the hash computing. isTerminal()233 abstract boolean isTerminal(); 234 235 // The name of the request header that will be used to obtain the hash key. 236 // Only valid if type is HEADER. 237 @Nullable headerName()238 abstract String headerName(); 239 240 // The regular expression used to find portions to be replaced in the header value. 241 // Only valid if type is HEADER. 242 @Nullable regEx()243 abstract Pattern regEx(); 244 245 // The string that should be substituted into matching portions of the header value. 246 // Only valid if type is HEADER. 247 @Nullable regExSubstitution()248 abstract String regExSubstitution(); 249 forHeader(boolean isTerminal, String headerName, @Nullable Pattern regEx, @Nullable String regExSubstitution)250 static HashPolicy forHeader(boolean isTerminal, String headerName, 251 @Nullable Pattern regEx, @Nullable String regExSubstitution) { 252 checkNotNull(headerName, "headerName"); 253 return HashPolicy.create(Type.HEADER, isTerminal, headerName, regEx, regExSubstitution); 254 } 255 forChannelId(boolean isTerminal)256 static HashPolicy forChannelId(boolean isTerminal) { 257 return HashPolicy.create(Type.CHANNEL_ID, isTerminal, null, null, null); 258 } 259 create(Type type, boolean isTerminal, @Nullable String headerName, @Nullable Pattern regEx, @Nullable String regExSubstitution)260 private static HashPolicy create(Type type, boolean isTerminal, @Nullable String headerName, 261 @Nullable Pattern regEx, @Nullable String regExSubstitution) { 262 return new AutoValue_VirtualHost_Route_RouteAction_HashPolicy(type, isTerminal, 263 headerName, regEx, regExSubstitution); 264 } 265 266 enum Type { 267 HEADER, CHANNEL_ID 268 } 269 } 270 271 @AutoValue 272 abstract static class RetryPolicy { maxAttempts()273 abstract int maxAttempts(); 274 retryableStatusCodes()275 abstract ImmutableList<Code> retryableStatusCodes(); 276 initialBackoff()277 abstract Duration initialBackoff(); 278 maxBackoff()279 abstract Duration maxBackoff(); 280 281 @Nullable perAttemptRecvTimeout()282 abstract Duration perAttemptRecvTimeout(); 283 create( int maxAttempts, List<Code> retryableStatusCodes, Duration initialBackoff, Duration maxBackoff, @Nullable Duration perAttemptRecvTimeout)284 static RetryPolicy create( 285 int maxAttempts, List<Code> retryableStatusCodes, Duration initialBackoff, 286 Duration maxBackoff, @Nullable Duration perAttemptRecvTimeout) { 287 return new AutoValue_VirtualHost_Route_RouteAction_RetryPolicy( 288 maxAttempts, 289 ImmutableList.copyOf(retryableStatusCodes), 290 initialBackoff, 291 maxBackoff, 292 perAttemptRecvTimeout); 293 } 294 } 295 } 296 } 297 } 298