xref: /aosp_15_r20/external/grpc-grpc-java/xds/src/main/java/io/grpc/xds/VirtualHost.java (revision e07d83d3ffcef9ecfc9f7f475418ec639ff0e5fe)
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