Skip to content

Commit 036c7f0

Browse files
xds: Refactor to allow common code usage between the ext_proc client and server interceptors (#12883)
Moved some of the util classes for ext_proc to a new sub package io.grpc.xds.internal.extproc.
1 parent d49c0b1 commit 036c7f0

11 files changed

Lines changed: 731 additions & 431 deletions

xds/src/main/java/io/grpc/xds/ExternalProcessorClientInterceptor.java

Lines changed: 84 additions & 369 deletions
Large diffs are not rendered by default.

xds/src/main/java/io/grpc/xds/ExternalProcessorFilter.java

Lines changed: 1 addition & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,17 @@
2929
import io.envoyproxy.envoy.extensions.filters.http.ext_proc.v3.ExtProcOverrides;
3030
import io.envoyproxy.envoy.extensions.filters.http.ext_proc.v3.ExtProcPerRoute;
3131
import io.envoyproxy.envoy.extensions.filters.http.ext_proc.v3.ExternalProcessor;
32-
import io.envoyproxy.envoy.extensions.filters.http.ext_proc.v3.HeaderForwardingRules;
3332
import io.envoyproxy.envoy.extensions.filters.http.ext_proc.v3.ProcessingMode;
3433
import io.grpc.ClientInterceptor;
3534
import io.grpc.internal.GrpcUtil;
36-
import io.grpc.xds.Filter.FilterConfigParseContext;
37-
import io.grpc.xds.Filter.FilterContext;
38-
import io.grpc.xds.internal.MatcherParser;
39-
import io.grpc.xds.internal.Matchers;
35+
import io.grpc.xds.internal.HeaderForwardingRulesConfig;
4036
import io.grpc.xds.internal.grpcservice.CachedChannelManager;
4137
import io.grpc.xds.internal.grpcservice.GrpcServiceConfig;
4238
import io.grpc.xds.internal.grpcservice.GrpcServiceParseException;
4339
import io.grpc.xds.internal.headermutations.HeaderMutationRulesConfig;
4440
import io.grpc.xds.internal.headermutations.HeaderMutationRulesParseException;
4541
import io.grpc.xds.internal.headermutations.HeaderMutationRulesParser;
4642
import java.util.List;
47-
import java.util.Locale;
4843
import java.util.Optional;
4944
import java.util.concurrent.ScheduledExecutorService;
5045
import java.util.concurrent.TimeUnit;
@@ -397,51 +392,5 @@ GrpcServiceConfig getGrpcServiceConfig() {
397392
}
398393
}
399394

400-
static final class HeaderForwardingRulesConfig {
401-
private final ImmutableList<Matchers.StringMatcher> allowedHeaders;
402-
private final ImmutableList<Matchers.StringMatcher> disallowedHeaders;
403395

404-
HeaderForwardingRulesConfig(
405-
ImmutableList<Matchers.StringMatcher> allowedHeaders,
406-
ImmutableList<Matchers.StringMatcher> disallowedHeaders) {
407-
this.allowedHeaders = checkNotNull(allowedHeaders, "allowedHeaders");
408-
this.disallowedHeaders = checkNotNull(disallowedHeaders, "disallowedHeaders");
409-
}
410-
411-
static HeaderForwardingRulesConfig create(HeaderForwardingRules proto) {
412-
ImmutableList<Matchers.StringMatcher> allowedHeaders = ImmutableList.of();
413-
if (proto.hasAllowedHeaders()) {
414-
allowedHeaders = MatcherParser.parseListStringMatcher(proto.getAllowedHeaders());
415-
}
416-
ImmutableList<Matchers.StringMatcher> disallowedHeaders = ImmutableList.of();
417-
if (proto.hasDisallowedHeaders()) {
418-
disallowedHeaders = MatcherParser.parseListStringMatcher(proto.getDisallowedHeaders());
419-
}
420-
return new HeaderForwardingRulesConfig(allowedHeaders, disallowedHeaders);
421-
}
422-
423-
boolean isAllowed(String headerName) {
424-
String lowerHeaderName = headerName.toLowerCase(Locale.ROOT);
425-
if (!allowedHeaders.isEmpty()) {
426-
boolean matched = false;
427-
for (Matchers.StringMatcher matcher : allowedHeaders) {
428-
if (matcher.matches(lowerHeaderName)) {
429-
matched = true;
430-
break;
431-
}
432-
}
433-
if (!matched) {
434-
return false;
435-
}
436-
}
437-
if (!disallowedHeaders.isEmpty()) {
438-
for (Matchers.StringMatcher matcher : disallowedHeaders) {
439-
if (matcher.matches(lowerHeaderName)) {
440-
return false;
441-
}
442-
}
443-
}
444-
return true;
445-
}
446-
}
447396
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright 2026 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.internal;
18+
19+
import static com.google.common.base.Preconditions.checkNotNull;
20+
21+
import com.google.common.collect.ImmutableList;
22+
import io.envoyproxy.envoy.extensions.filters.http.ext_proc.v3.HeaderForwardingRules;
23+
import java.util.Locale;
24+
25+
/**
26+
* Configuration for header forwarding rules in external processing.
27+
*/
28+
public final class HeaderForwardingRulesConfig {
29+
private final ImmutableList<Matchers.StringMatcher> allowedHeaders;
30+
private final ImmutableList<Matchers.StringMatcher> disallowedHeaders;
31+
32+
public HeaderForwardingRulesConfig(
33+
ImmutableList<Matchers.StringMatcher> allowedHeaders,
34+
ImmutableList<Matchers.StringMatcher> disallowedHeaders) {
35+
this.allowedHeaders = checkNotNull(allowedHeaders, "allowedHeaders");
36+
this.disallowedHeaders = checkNotNull(disallowedHeaders, "disallowedHeaders");
37+
}
38+
39+
public static HeaderForwardingRulesConfig create(HeaderForwardingRules proto) {
40+
ImmutableList<Matchers.StringMatcher> allowedHeaders = ImmutableList.of();
41+
if (proto.hasAllowedHeaders()) {
42+
allowedHeaders = MatcherParser.parseListStringMatcher(proto.getAllowedHeaders());
43+
}
44+
ImmutableList<Matchers.StringMatcher> disallowedHeaders = ImmutableList.of();
45+
if (proto.hasDisallowedHeaders()) {
46+
disallowedHeaders = MatcherParser.parseListStringMatcher(proto.getDisallowedHeaders());
47+
}
48+
return new HeaderForwardingRulesConfig(allowedHeaders, disallowedHeaders);
49+
}
50+
51+
public boolean isAllowed(String headerName) {
52+
String lowerHeaderName = headerName.toLowerCase(Locale.ROOT);
53+
if (!allowedHeaders.isEmpty()) {
54+
boolean matched = false;
55+
for (Matchers.StringMatcher matcher : allowedHeaders) {
56+
if (matcher.matches(lowerHeaderName)) {
57+
matched = true;
58+
break;
59+
}
60+
}
61+
if (!matched) {
62+
return false;
63+
}
64+
}
65+
if (!disallowedHeaders.isEmpty()) {
66+
for (Matchers.StringMatcher matcher : disallowedHeaders) {
67+
if (matcher.matches(lowerHeaderName)) {
68+
return false;
69+
}
70+
}
71+
}
72+
return true;
73+
}
74+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2026 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.internal.extproc;
18+
19+
/**
20+
* States of the call inside the data plane.
21+
*/
22+
public enum DataPlaneCallState {
23+
IDLE,
24+
ACTIVE,
25+
CLOSED
26+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2026 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.internal.extproc;
18+
19+
/**
20+
* The event type representing the phase of external processing.
21+
*/
22+
public enum EventType {
23+
REQUEST_HEADERS,
24+
REQUEST_BODY,
25+
RESPONSE_HEADERS,
26+
RESPONSE_BODY,
27+
RESPONSE_TRAILERS
28+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2026 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.internal.extproc;
18+
19+
/**
20+
* States of the stream with the external processor.
21+
*/
22+
public enum ExtProcStreamState {
23+
ACTIVE,
24+
DRAINING,
25+
COMPLETED,
26+
FAILED;
27+
28+
public boolean isCompleted() {
29+
return this == COMPLETED || this == FAILED;
30+
}
31+
32+
public boolean isFailed() {
33+
return this == FAILED;
34+
}
35+
36+
public boolean isDraining() {
37+
return this == DRAINING;
38+
}
39+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2026 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.internal.extproc;
18+
19+
import com.google.common.annotations.VisibleForTesting;
20+
import com.google.common.collect.ImmutableList;
21+
import io.grpc.DoubleHistogramMetricInstrument;
22+
import io.grpc.MetricInstrumentRegistry;
23+
import io.grpc.internal.GrpcUtil;
24+
import java.util.List;
25+
26+
/**
27+
* Holds metric instrument definitions for server-side external processing.
28+
*/
29+
public final class ExternalProcessorServerInterceptorMetricInstruments {
30+
@VisibleForTesting
31+
public static DoubleHistogramMetricInstrument clientHeadersDuration;
32+
@VisibleForTesting
33+
public static DoubleHistogramMetricInstrument clientHalfCloseDuration;
34+
@VisibleForTesting
35+
public static DoubleHistogramMetricInstrument serverHeadersDuration;
36+
@VisibleForTesting
37+
public static DoubleHistogramMetricInstrument serverTrailersDuration;
38+
39+
// Copied from io.grpc.opentelemetry.internal.OpenTelemetryConstants.LATENCY_BUCKETS
40+
private static final List<Double> LATENCY_BUCKETS = ImmutableList.of(
41+
0d, 0.00001d, 0.00005d, 0.0001d, 0.0003d, 0.0006d, 0.0008d, 0.001d, 0.002d,
42+
0.003d, 0.004d, 0.005d, 0.006d, 0.008d, 0.01d, 0.013d, 0.016d, 0.02d,
43+
0.025d, 0.03d, 0.04d, 0.05d, 0.065d, 0.08d, 0.1d, 0.13d, 0.16d,
44+
0.2d, 0.25d, 0.3d, 0.4d, 0.5d, 0.65d, 0.8d, 1d, 2d,
45+
5d, 10d, 20d, 50d, 100d);
46+
47+
static {
48+
initMetricInstruments();
49+
}
50+
51+
public static synchronized void initMetricInstruments() {
52+
if (GrpcUtil.getFlag("GRPC_EXPERIMENTAL_XDS_EXT_PROC_ON_SERVER", false)) {
53+
if (clientHeadersDuration == null) {
54+
MetricInstrumentRegistry registry = MetricInstrumentRegistry.getDefaultRegistry();
55+
56+
clientHeadersDuration = registry.registerDoubleHistogram(
57+
"grpc.server_ext_proc.client_headers_duration",
58+
"Time between when the ext_proc filter sees the client's headers and when "
59+
+ "it allows those headers to continue on to the next filter",
60+
"s",
61+
LATENCY_BUCKETS,
62+
ImmutableList.of(),
63+
ImmutableList.of(),
64+
true);
65+
66+
clientHalfCloseDuration = registry.registerDoubleHistogram(
67+
"grpc.server_ext_proc.client_half_close_duration",
68+
"Time between when the ext_proc filter sees the client's half-close and when "
69+
+ "it allows that half-close to continue on to the next filter",
70+
"s",
71+
LATENCY_BUCKETS,
72+
ImmutableList.of(),
73+
ImmutableList.of(),
74+
true);
75+
76+
serverHeadersDuration = registry.registerDoubleHistogram(
77+
"grpc.server_ext_proc.server_headers_duration",
78+
"Time between when the ext_proc filter sees the server's headers and when "
79+
+ "it allows those headers to continue on to the next filter",
80+
"s",
81+
LATENCY_BUCKETS,
82+
ImmutableList.of(),
83+
ImmutableList.of(),
84+
true);
85+
86+
serverTrailersDuration = registry.registerDoubleHistogram(
87+
"grpc.server_ext_proc.server_trailers_duration",
88+
"Time between when the ext_proc filter sees the server's trailers and when "
89+
+ "it allows those trailers to continue on to the next filter",
90+
"s",
91+
LATENCY_BUCKETS,
92+
ImmutableList.of(),
93+
ImmutableList.of(),
94+
true);
95+
}
96+
}
97+
}
98+
99+
private ExternalProcessorServerInterceptorMetricInstruments() {}
100+
}

0 commit comments

Comments
 (0)