Skip to content

Commit 522db6e

Browse files
committed
Moved some of the util classes for ext_proc to a new sub package io.grpc.xds.internal.extproc, with public access modifier:
1. Otel metric instruments out of the client interceptor 2. Moved `KnownLengthInputStream` used during observability mode to send message to the data plane out of the client interceptor. Created a new class io.grpc.xds.ExternalProcessorUtil with the below. This class is kept here and not in the above sub-package because `toHeaderMap` operates on `HeaderMutationRulesConfig` which is package-private. 1. Method `outboundStreamFromByteString` used to construct the ProcessingRequest payload to Util as a static utility method. 2. Method `toHeaderMap` to Util.
1 parent 71a10dc commit 522db6e

7 files changed

Lines changed: 215 additions & 210 deletions

File tree

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

Lines changed: 12 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
package io.grpc.xds;
1818

1919
import static com.google.common.base.Preconditions.checkNotNull;
20+
import static io.grpc.xds.ExternalProcessorUtil.outboundStreamToByteString;
21+
import static io.grpc.xds.ExternalProcessorUtil.toHeaderMap;
22+
import static io.grpc.xds.internal.extproc.ExternalProcessorMetricInstruments.clientHalfCloseDuration;
23+
import static io.grpc.xds.internal.extproc.ExternalProcessorMetricInstruments.clientHeadersDuration;
24+
import static io.grpc.xds.internal.extproc.ExternalProcessorMetricInstruments.serverHeadersDuration;
25+
import static io.grpc.xds.internal.extproc.ExternalProcessorMetricInstruments.serverTrailersDuration;
2026

2127
import com.google.common.annotations.VisibleForTesting;
2228
import com.google.common.base.Joiner;
@@ -26,7 +32,6 @@
2632
import com.google.protobuf.ByteString;
2733
import com.google.protobuf.Struct;
2834
import com.google.protobuf.Value;
29-
import io.envoyproxy.envoy.config.core.v3.HeaderMap;
3035
import io.envoyproxy.envoy.extensions.filters.http.ext_proc.v3.ProcessingMode;
3136
import io.envoyproxy.envoy.service.ext_proc.v3.BodyMutation;
3237
import io.envoyproxy.envoy.service.ext_proc.v3.BodyResponse;
@@ -48,25 +53,24 @@
4853
import io.grpc.Context;
4954
import io.grpc.Deadline;
5055
import io.grpc.DoubleHistogramMetricInstrument;
51-
import io.grpc.Drainable;
5256
import io.grpc.ForwardingClientCall.SimpleForwardingClientCall;
5357
import io.grpc.ForwardingClientCallListener.SimpleForwardingClientCallListener;
54-
import io.grpc.KnownLength;
5558
import io.grpc.ManagedChannel;
5659
import io.grpc.Metadata;
5760
import io.grpc.MethodDescriptor;
58-
import io.grpc.MetricInstrumentRegistry;
5961
import io.grpc.MetricRecorder;
6062
import io.grpc.Status;
6163
import io.grpc.StatusRuntimeException;
6264
import io.grpc.internal.DelayedClientCall;
63-
import io.grpc.internal.GrpcUtil;
6465
import io.grpc.internal.SerializingExecutor;
6566
import io.grpc.stub.ClientCallStreamObserver;
6667
import io.grpc.stub.ClientResponseObserver;
68+
import io.grpc.xds.ExternalProcessorFilter.DataPlaneCallState;
69+
import io.grpc.xds.ExternalProcessorFilter.ExtProcStreamState;
6770
import io.grpc.xds.ExternalProcessorFilter.ExternalProcessorFilterConfig;
68-
import io.grpc.xds.ExternalProcessorFilter.HeaderForwardingRulesConfig;
6971
import io.grpc.xds.Filter.FilterContext;
72+
import io.grpc.xds.internal.extproc.ExternalProcessorMetricInstruments;
73+
import io.grpc.xds.internal.extproc.KnownLengthInputStream;
7074
import io.grpc.xds.internal.grpcservice.CachedChannelManager;
7175
import io.grpc.xds.internal.grpcservice.HeaderValue;
7276
import io.grpc.xds.internal.grpcservice.HeaderValueValidationUtils;
@@ -98,76 +102,6 @@
98102
*/
99103
final class ExternalProcessorClientInterceptor implements ClientInterceptor {
100104

101-
102-
103-
@VisibleForTesting
104-
static final DoubleHistogramMetricInstrument clientHeadersDuration;
105-
@VisibleForTesting
106-
static final DoubleHistogramMetricInstrument clientHalfCloseDuration;
107-
@VisibleForTesting
108-
static final DoubleHistogramMetricInstrument serverHeadersDuration;
109-
@VisibleForTesting
110-
static final DoubleHistogramMetricInstrument serverTrailersDuration;
111-
112-
// Copied from io.grpc.opentelemetry.internal.OpenTelemetryConstants.LATENCY_BUCKETS
113-
private static final List<Double> LATENCY_BUCKETS = ImmutableList.of(
114-
0d, 0.00001d, 0.00005d, 0.0001d, 0.0003d, 0.0006d, 0.0008d, 0.001d, 0.002d,
115-
0.003d, 0.004d, 0.005d, 0.006d, 0.008d, 0.01d, 0.013d, 0.016d, 0.02d,
116-
0.025d, 0.03d, 0.04d, 0.05d, 0.065d, 0.08d, 0.1d, 0.13d, 0.16d,
117-
0.2d, 0.25d, 0.3d, 0.4d, 0.5d, 0.65d, 0.8d, 1d, 2d,
118-
5d, 10d, 20d, 50d, 100d);
119-
120-
static {
121-
if (GrpcUtil.getFlag("GRPC_EXPERIMENTAL_XDS_EXT_PROC_ON_CLIENT", false)) {
122-
MetricInstrumentRegistry registry = MetricInstrumentRegistry.getDefaultRegistry();
123-
124-
clientHeadersDuration = registry.registerDoubleHistogram(
125-
"grpc.client_ext_proc.client_headers_duration",
126-
"Time between when the ext_proc filter sees the client's headers and when "
127-
+ "it allows those headers to continue on to the next filter",
128-
"s",
129-
LATENCY_BUCKETS,
130-
ImmutableList.of("grpc.target"),
131-
ImmutableList.of("grpc.lb.backend_service"),
132-
true);
133-
134-
clientHalfCloseDuration = registry.registerDoubleHistogram(
135-
"grpc.client_ext_proc.client_half_close_duration",
136-
"Time between when the ext_proc filter sees the client's half-close and when "
137-
+ "it allows that half-close to continue on to the next filter",
138-
"s",
139-
LATENCY_BUCKETS,
140-
ImmutableList.of("grpc.target"),
141-
ImmutableList.of("grpc.lb.backend_service"),
142-
true);
143-
144-
serverHeadersDuration = registry.registerDoubleHistogram(
145-
"grpc.client_ext_proc.server_headers_duration",
146-
"Time between when the ext_proc filter sees the server's headers and when "
147-
+ "it allows those headers to continue on to the next filter",
148-
"s",
149-
LATENCY_BUCKETS,
150-
ImmutableList.of("grpc.target"),
151-
ImmutableList.of("grpc.lb.backend_service"),
152-
true);
153-
154-
serverTrailersDuration = registry.registerDoubleHistogram(
155-
"grpc.client_ext_proc.server_trailers_duration",
156-
"Time between when the ext_proc filter sees the server's trailers and when "
157-
+ "it allows those trailers to continue on to the next filter",
158-
"s",
159-
LATENCY_BUCKETS,
160-
ImmutableList.of("grpc.target"),
161-
ImmutableList.of("grpc.lb.backend_service"),
162-
true);
163-
} else {
164-
clientHeadersDuration = null;
165-
clientHalfCloseDuration = null;
166-
serverHeadersDuration = null;
167-
serverTrailersDuration = null;
168-
}
169-
}
170-
171105
private final ExternalProcessorFilterConfig filterConfig;
172106
private final ScheduledExecutorService scheduler;
173107
private final MetricRecorder metricsRecorder;
@@ -182,6 +116,7 @@ final class ExternalProcessorClientInterceptor implements ClientInterceptor {
182116
checkNotNull(cachedChannelManager, "cachedChannelManager");
183117
this.scheduler = checkNotNull(scheduler, "scheduler");
184118
this.metricsRecorder = checkNotNull(context.metricsRecorder(), "metricsRecorder");
119+
ExternalProcessorMetricInstruments.initMetricInstruments();
185120
this.extProcChannel = cachedChannelManager.getChannel(filterConfig.getGrpcServiceConfig());
186121
}
187122

@@ -259,48 +194,6 @@ public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
259194
}
260195

261196
// --- SHARED UTILITY METHODS ---
262-
private static HeaderMap toHeaderMap(
263-
Metadata metadata, Optional<HeaderForwardingRulesConfig> forwardRules) {
264-
HeaderMap.Builder builder =
265-
HeaderMap.newBuilder();
266-
267-
for (String key : metadata.keys()) {
268-
if (forwardRules.isPresent() && !forwardRules.get().isAllowed(key)) {
269-
continue;
270-
}
271-
// Map binary headers using raw_value
272-
if (key.endsWith(Metadata.BINARY_HEADER_SUFFIX)) {
273-
Metadata.Key<byte[]> binKey = Metadata.Key.of(key, Metadata.BINARY_BYTE_MARSHALLER);
274-
Iterable<byte[]> values = metadata.getAll(binKey);
275-
if (values != null) {
276-
for (byte[] binValue : values) {
277-
String base64Value = BaseEncoding.base64().encode(binValue);
278-
io.envoyproxy.envoy.config.core.v3.HeaderValue headerValue =
279-
io.envoyproxy.envoy.config.core.v3.HeaderValue.newBuilder()
280-
.setKey(key.toLowerCase(Locale.ROOT))
281-
.setRawValue(ByteString.copyFromUtf8(base64Value))
282-
.build();
283-
builder.addHeaders(headerValue);
284-
}
285-
}
286-
} else {
287-
Metadata.Key<String> asciiKey = Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER);
288-
Iterable<String> values = metadata.getAll(asciiKey);
289-
if (values != null) {
290-
for (String value : values) {
291-
io.envoyproxy.envoy.config.core.v3.HeaderValue headerValue =
292-
io.envoyproxy.envoy.config.core.v3.HeaderValue.newBuilder()
293-
.setKey(key.toLowerCase(Locale.ROOT))
294-
.setRawValue(ByteString.copyFromUtf8(value))
295-
.build();
296-
builder.addHeaders(headerValue);
297-
}
298-
}
299-
}
300-
}
301-
return builder.build();
302-
}
303-
304197
private static ImmutableMap<String, Struct> collectAttributes(
305198
ImmutableList<String> requestedAttributes,
306199
MethodDescriptor<?, ?> method,
@@ -407,31 +300,6 @@ private static class DataPlaneDelayedCall<ReqT, RespT> extends DelayedClientCall
407300
*/
408301
private static class DataPlaneClientCall
409302
extends SimpleForwardingClientCall<InputStream, InputStream> {
410-
enum ExtProcStreamState {
411-
ACTIVE,
412-
DRAINING,
413-
COMPLETED,
414-
FAILED;
415-
416-
boolean isCompleted() {
417-
return this == COMPLETED || this == FAILED;
418-
}
419-
420-
boolean isFailed() {
421-
return this == FAILED;
422-
}
423-
424-
boolean isDraining() {
425-
return this == DRAINING;
426-
}
427-
}
428-
429-
enum DataPlaneCallState {
430-
IDLE,
431-
ACTIVE,
432-
CLOSED
433-
}
434-
435303
private enum EventType {
436304
REQUEST_HEADERS,
437305
REQUEST_BODY,
@@ -1395,7 +1263,7 @@ public void onMessage(InputStream message) {
13951263
@Override
13961264
public void onClose(Status status, Metadata trailers) {
13971265
dataPlaneClientCall.setServerTrailersStartNanos(System.nanoTime());
1398-
DataPlaneClientCall.ExtProcStreamState extProcStreamState =
1266+
ExtProcStreamState extProcStreamState =
13991267
dataPlaneClientCall.getExtProcStreamState().get();
14001268
if (extProcStreamState.isFailed()
14011269
&& !dataPlaneClientCall.getConfig().getObservabilityMode()
@@ -1593,46 +1461,4 @@ private void sendResponseBodyToExtProc(
15931461
.build());
15941462
}
15951463
}
1596-
1597-
1598-
1599-
private static ByteString outboundStreamToByteString(InputStream message) throws IOException {
1600-
if (message instanceof Drainable) {
1601-
int size = message.available();
1602-
ByteString.Output output =
1603-
size > 0 ? ByteString.newOutput(size) : ByteString.newOutput();
1604-
((Drainable) message).drainTo(output);
1605-
return output.toByteString();
1606-
}
1607-
return ByteString.readFrom(message);
1608-
}
1609-
1610-
private static final class KnownLengthInputStream extends InputStream
1611-
implements KnownLength {
1612-
private final InputStream delegate;
1613-
1614-
KnownLengthInputStream(ByteString byteString) {
1615-
this.delegate = byteString.newInput();
1616-
}
1617-
1618-
@Override
1619-
public int read() throws IOException {
1620-
return delegate.read();
1621-
}
1622-
1623-
@Override
1624-
public int read(byte[] b, int off, int len) throws IOException {
1625-
return delegate.read(b, off, len);
1626-
}
1627-
1628-
@Override
1629-
public int available() throws IOException {
1630-
return delegate.available();
1631-
}
1632-
1633-
@Override
1634-
public void close() throws IOException {
1635-
delegate.close();
1636-
}
1637-
}
16381464
}

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

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,9 @@
3333
import io.envoyproxy.envoy.extensions.filters.http.ext_proc.v3.ProcessingMode;
3434
import io.grpc.ClientInterceptor;
3535
import io.grpc.internal.GrpcUtil;
36-
import io.grpc.xds.Filter.FilterConfigParseContext;
37-
import io.grpc.xds.Filter.FilterContext;
3836
import io.grpc.xds.internal.MatcherParser;
3937
import io.grpc.xds.internal.Matchers;
38+
import io.grpc.xds.internal.extproc.ExternalProcessorMetricInstruments;
4039
import io.grpc.xds.internal.grpcservice.CachedChannelManager;
4140
import io.grpc.xds.internal.grpcservice.GrpcServiceConfig;
4241
import io.grpc.xds.internal.grpcservice.GrpcServiceParseException;
@@ -57,6 +56,35 @@ public class ExternalProcessorFilter implements Filter {
5756
static final String TYPE_URL =
5857
"type.googleapis.com/envoy.extensions.filters.http.ext_proc.v3.ExternalProcessor";
5958

59+
static {
60+
ExternalProcessorMetricInstruments.initMetricInstruments();
61+
}
62+
63+
enum ExtProcStreamState {
64+
ACTIVE,
65+
DRAINING,
66+
COMPLETED,
67+
FAILED;
68+
69+
boolean isCompleted() {
70+
return this == COMPLETED || this == FAILED;
71+
}
72+
73+
boolean isFailed() {
74+
return this == FAILED;
75+
}
76+
77+
boolean isDraining() {
78+
return this == DRAINING;
79+
}
80+
}
81+
82+
enum DataPlaneCallState {
83+
IDLE,
84+
ACTIVE,
85+
CLOSED
86+
}
87+
6088
private final CachedChannelManager cachedChannelManager;
6189
private final FilterContext context;
6290

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@
2525
* A registry for all supported {@link Filter}s. Filters can be queried from the registry
2626
* by any of the {@link Filter.Provider#typeUrls() type URLs}.
2727
*/
28-
final class FilterRegistry {
28+
public final class FilterRegistry {
2929
private static FilterRegistry instance;
3030

3131
private final Map<String, Filter.Provider> supportedFilters = new HashMap<>();
3232

3333
private FilterRegistry() {}
3434

35-
static synchronized FilterRegistry getDefaultRegistry() {
35+
public static synchronized FilterRegistry getDefaultRegistry() {
3636
if (instance == null) {
3737
instance = newRegistry().register(
3838
new FaultFilter.Provider(),
@@ -45,7 +45,7 @@ static synchronized FilterRegistry getDefaultRegistry() {
4545
}
4646

4747
@VisibleForTesting
48-
static synchronized void reset() {
48+
public static synchronized void reset() {
4949
instance = null;
5050
}
5151

@@ -65,7 +65,7 @@ FilterRegistry register(Filter.Provider... filters) {
6565
}
6666

6767
@Nullable
68-
Filter.Provider get(String typeUrl) {
68+
public Filter.Provider get(String typeUrl) {
6969
return supportedFilters.get(typeUrl);
7070
}
7171
}

0 commit comments

Comments
 (0)