1717package io .grpc .xds ;
1818
1919import 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
2127import com .google .common .annotations .VisibleForTesting ;
2228import com .google .common .base .Joiner ;
2632import com .google .protobuf .ByteString ;
2733import com .google .protobuf .Struct ;
2834import com .google .protobuf .Value ;
29- import io .envoyproxy .envoy .config .core .v3 .HeaderMap ;
3035import io .envoyproxy .envoy .extensions .filters .http .ext_proc .v3 .ProcessingMode ;
3136import io .envoyproxy .envoy .service .ext_proc .v3 .BodyMutation ;
3237import io .envoyproxy .envoy .service .ext_proc .v3 .BodyResponse ;
4853import io .grpc .Context ;
4954import io .grpc .Deadline ;
5055import io .grpc .DoubleHistogramMetricInstrument ;
51- import io .grpc .Drainable ;
5256import io .grpc .ForwardingClientCall .SimpleForwardingClientCall ;
5357import io .grpc .ForwardingClientCallListener .SimpleForwardingClientCallListener ;
54- import io .grpc .KnownLength ;
5558import io .grpc .ManagedChannel ;
5659import io .grpc .Metadata ;
5760import io .grpc .MethodDescriptor ;
58- import io .grpc .MetricInstrumentRegistry ;
5961import io .grpc .MetricRecorder ;
6062import io .grpc .Status ;
6163import io .grpc .StatusRuntimeException ;
6264import io .grpc .internal .DelayedClientCall ;
63- import io .grpc .internal .GrpcUtil ;
6465import io .grpc .internal .SerializingExecutor ;
6566import io .grpc .stub .ClientCallStreamObserver ;
6667import io .grpc .stub .ClientResponseObserver ;
68+ import io .grpc .xds .ExternalProcessorFilter .DataPlaneCallState ;
69+ import io .grpc .xds .ExternalProcessorFilter .ExtProcStreamState ;
6770import io .grpc .xds .ExternalProcessorFilter .ExternalProcessorFilterConfig ;
68- import io .grpc .xds .ExternalProcessorFilter .HeaderForwardingRulesConfig ;
6971import io .grpc .xds .Filter .FilterContext ;
72+ import io .grpc .xds .internal .extproc .ExternalProcessorMetricInstruments ;
73+ import io .grpc .xds .internal .extproc .KnownLengthInputStream ;
7074import io .grpc .xds .internal .grpcservice .CachedChannelManager ;
7175import io .grpc .xds .internal .grpcservice .HeaderValue ;
7276import io .grpc .xds .internal .grpcservice .HeaderValueValidationUtils ;
98102 */
99103final 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}
0 commit comments