@@ -6717,8 +6717,213 @@ public void onClose(Status status, Metadata trailers) {
67176717 clientCall .sendMessage (new ByteArrayInputStream ("hello" .getBytes (StandardCharsets .UTF_8 )));
67186718 clientCall .halfClose ();
67196719
6720+ assertThat (closedStatus .get ().getCode ()).isEqualTo (Status .Code .INTERNAL );
6721+ assertThat (closedStatus .get ().getDescription ()).contains ("External processor stream failed" );
6722+ }
6723+
6724+ @ Test
6725+ public void givenFailureModeAllowTrue_whenExtProcStreamFailsAfterRequestBodySent_thenCallFails ()
6726+ throws Exception {
6727+ ExternalProcessor proto = createBaseProto (extProcServerName )
6728+ .setFailureModeAllow (true )
6729+ .setProcessingMode (ProcessingMode .newBuilder ()
6730+ .setRequestHeaderMode (ProcessingMode .HeaderSendMode .SEND )
6731+ .setRequestBodyMode (ProcessingMode .BodySendMode .GRPC )
6732+ .setResponseHeaderMode (ProcessingMode .HeaderSendMode .SKIP )
6733+ .setResponseBodyMode (ProcessingMode .BodySendMode .NONE )
6734+ .setResponseTrailerMode (ProcessingMode .HeaderSendMode .SKIP )
6735+ .build ())
6736+ .build ();
6737+ ConfigOrError <ExternalProcessorFilterConfig > configOrError =
6738+ provider .parseFilterConfig (Any .pack (proto ), filterContext );
6739+ assertThat (configOrError .errorDetail ).isNull ();
6740+ ExternalProcessorFilterConfig filterConfig = configOrError .config ;
6741+
6742+ final CountDownLatch extProcLatch = new CountDownLatch (1 );
6743+ final AtomicReference <StreamObserver <ProcessingResponse >> responseObserverRef =
6744+ new AtomicReference <>();
6745+ ExternalProcessorGrpc .ExternalProcessorImplBase extProcImpl =
6746+ new ExternalProcessorGrpc .ExternalProcessorImplBase () {
6747+ @ Override
6748+ public StreamObserver <ProcessingRequest > process (
6749+ final StreamObserver <ProcessingResponse > responseObserver ) {
6750+ responseObserverRef .set (responseObserver );
6751+ ((ServerCallStreamObserver <ProcessingResponse >) responseObserver ).request (100 );
6752+ return new StreamObserver <ProcessingRequest >() {
6753+ @ Override
6754+ public void onNext (ProcessingRequest request ) {
6755+ if (request .hasRequestHeaders ()) {
6756+ responseObserver .onNext (ProcessingResponse .newBuilder ()
6757+ .setRequestHeaders (HeadersResponse .newBuilder ().build ())
6758+ .build ());
6759+ } else if (request .hasRequestBody ()) {
6760+ // Fail the stream after receiving the request body
6761+ responseObserver .onError (
6762+ Status .INTERNAL .withDescription ("Simulated stream failure" ).asRuntimeException ());
6763+ extProcLatch .countDown ();
6764+ }
6765+ }
6766+
6767+ @ Override
6768+ public void onError (Throwable t ) {}
6769+
6770+ @ Override
6771+ public void onCompleted () {}
6772+ };
6773+ }
6774+ };
6775+
6776+ grpcCleanup .register (InProcessServerBuilder .forName (extProcServerName )
6777+ .addService (extProcImpl )
6778+ .directExecutor ()
6779+ .build ().start ());
6780+
6781+ CachedChannelManager channelManager = new CachedChannelManager (config -> {
6782+ return grpcCleanup .register (
6783+ InProcessChannelBuilder .forName (extProcServerName )
6784+ .directExecutor ()
6785+ .build ());
6786+ });
6787+
6788+ ExternalProcessorServerInterceptor interceptor = new ExternalProcessorServerInterceptor (
6789+ filterConfig , channelManager , FAKE_CONTEXT );
6790+
6791+ final AtomicBoolean callStarted = new AtomicBoolean (false );
6792+ dataPlaneHandler = new DataPlaneServiceHandler () {
6793+ @ Override
6794+ public void sayHello (InputStream request , StreamObserver <InputStream > responseObserver ) {
6795+ callStarted .set (true );
6796+ responseObserver .onNext (request );
6797+ responseObserver .onCompleted ();
6798+ }
6799+ };
6800+
6801+ startDataPlane (interceptor );
6802+
6803+ io .grpc .ClientCall <InputStream , InputStream > clientCall = dataPlaneChannel .newCall (
6804+ METHOD_SAY_HELLO_RAW , io .grpc .CallOptions .DEFAULT );
6805+
6806+ final CountDownLatch callCompletedLatch = new CountDownLatch (1 );
6807+ final AtomicReference <Status > closedStatus = new AtomicReference <>();
6808+ clientCall .start (new io .grpc .ClientCall .Listener <InputStream >() {
6809+ @ Override
6810+ public void onClose (Status status , Metadata trailers ) {
6811+ closedStatus .set (status );
6812+ callCompletedLatch .countDown ();
6813+ }
6814+ }, new Metadata ());
6815+
6816+ clientCall .request (1 );
6817+ clientCall .sendMessage (new ByteArrayInputStream ("hello" .getBytes (StandardCharsets .UTF_8 )));
6818+ clientCall .halfClose ();
6819+
6820+ // Verify stream failed
6821+ assertThat (extProcLatch .await (5 , TimeUnit .SECONDS )).isTrue ();
6822+ // Verify call completed and failed with INTERNAL status (not fail-open)
67206823 assertThat (callCompletedLatch .await (5 , TimeUnit .SECONDS )).isTrue ();
6721- assertThat (closedStatus .get ().getCode ()).isEqualTo (Status .Code .UNAVAILABLE );
6824+ assertThat (callStarted .get ()).isFalse ();
6825+ assertThat (closedStatus .get ().getCode ()).isEqualTo (Status .Code .INTERNAL );
6826+ assertThat (closedStatus .get ().getDescription ()).contains ("External processor stream failed" );
6827+ }
6828+
6829+ @ Test
6830+ public void givenFailureModeAllowTrue_whenExtProcStreamFailsAfterResponseBodySent_thenCallFails ()
6831+ throws Exception {
6832+ ExternalProcessor proto = createBaseProto (extProcServerName )
6833+ .setFailureModeAllow (true )
6834+ .setProcessingMode (ProcessingMode .newBuilder ()
6835+ .setRequestHeaderMode (ProcessingMode .HeaderSendMode .SKIP )
6836+ .setRequestBodyMode (ProcessingMode .BodySendMode .NONE )
6837+ .setResponseHeaderMode (ProcessingMode .HeaderSendMode .SKIP )
6838+ .setResponseBodyMode (ProcessingMode .BodySendMode .GRPC )
6839+ .setResponseTrailerMode (ProcessingMode .HeaderSendMode .SEND )
6840+ .build ())
6841+ .build ();
6842+ ConfigOrError <ExternalProcessorFilterConfig > configOrError =
6843+ provider .parseFilterConfig (Any .pack (proto ), filterContext );
6844+ assertThat (configOrError .errorDetail ).isNull ();
6845+ ExternalProcessorFilterConfig filterConfig = configOrError .config ;
6846+
6847+ final CountDownLatch extProcLatch = new CountDownLatch (1 );
6848+ final AtomicReference <StreamObserver <ProcessingResponse >> responseObserverRef =
6849+ new AtomicReference <>();
6850+ ExternalProcessorGrpc .ExternalProcessorImplBase extProcImpl =
6851+ new ExternalProcessorGrpc .ExternalProcessorImplBase () {
6852+ @ Override
6853+ public StreamObserver <ProcessingRequest > process (
6854+ final StreamObserver <ProcessingResponse > responseObserver ) {
6855+ responseObserverRef .set (responseObserver );
6856+ ((ServerCallStreamObserver <ProcessingResponse >) responseObserver ).request (100 );
6857+ return new StreamObserver <ProcessingRequest >() {
6858+ @ Override
6859+ public void onNext (ProcessingRequest request ) {
6860+ if (request .hasResponseBody ()) {
6861+ // Fail the stream after receiving the response body
6862+ responseObserver .onError (
6863+ Status .INTERNAL .withDescription ("Simulated stream failure" ).asRuntimeException ());
6864+ extProcLatch .countDown ();
6865+ }
6866+ }
6867+
6868+ @ Override
6869+ public void onError (Throwable t ) {}
6870+
6871+ @ Override
6872+ public void onCompleted () {}
6873+ };
6874+ }
6875+ };
6876+
6877+ grpcCleanup .register (InProcessServerBuilder .forName (extProcServerName )
6878+ .addService (extProcImpl )
6879+ .directExecutor ()
6880+ .build ().start ());
6881+
6882+ CachedChannelManager channelManager = new CachedChannelManager (config -> {
6883+ return grpcCleanup .register (
6884+ InProcessChannelBuilder .forName (extProcServerName )
6885+ .directExecutor ()
6886+ .build ());
6887+ });
6888+
6889+ ExternalProcessorServerInterceptor interceptor = new ExternalProcessorServerInterceptor (
6890+ filterConfig , channelManager , FAKE_CONTEXT );
6891+
6892+ final AtomicBoolean callStarted = new AtomicBoolean (false );
6893+ dataPlaneHandler = new DataPlaneServiceHandler () {
6894+ @ Override
6895+ public void sayHello (InputStream request , StreamObserver <InputStream > responseObserver ) {
6896+ callStarted .set (true );
6897+ responseObserver .onNext (request );
6898+ responseObserver .onCompleted ();
6899+ }
6900+ };
6901+
6902+ startDataPlane (interceptor );
6903+
6904+ io .grpc .ClientCall <InputStream , InputStream > clientCall = dataPlaneChannel .newCall (
6905+ METHOD_SAY_HELLO_RAW , io .grpc .CallOptions .DEFAULT );
6906+
6907+ final CountDownLatch callCompletedLatch = new CountDownLatch (1 );
6908+ final AtomicReference <Status > closedStatus = new AtomicReference <>();
6909+ clientCall .start (new io .grpc .ClientCall .Listener <InputStream >() {
6910+ @ Override
6911+ public void onClose (Status status , Metadata trailers ) {
6912+ closedStatus .set (status );
6913+ callCompletedLatch .countDown ();
6914+ }
6915+ }, new Metadata ());
6916+
6917+ clientCall .request (1 );
6918+ clientCall .sendMessage (new ByteArrayInputStream ("hello" .getBytes (StandardCharsets .UTF_8 )));
6919+ clientCall .halfClose ();
6920+
6921+ // Verify stream failed
6922+ assertThat (extProcLatch .await (5 , TimeUnit .SECONDS )).isTrue ();
6923+ // Verify call completed and failed with INTERNAL status (not fail-open)
6924+ assertThat (callCompletedLatch .await (5 , TimeUnit .SECONDS )).isTrue ();
6925+ assertThat (callStarted .get ()).isTrue ();
6926+ assertThat (closedStatus .get ().getCode ()).isEqualTo (Status .Code .INTERNAL );
67226927 assertThat (closedStatus .get ().getDescription ()).contains ("External processor stream failed" );
67236928 }
67246929
@@ -8167,7 +8372,7 @@ public void onClose(Status status, Metadata trailers) {
81678372
81688373 assertThat (sidecarLatch .await (5 , TimeUnit .SECONDS )).isTrue ();
81698374 assertThat (callCompletedLatch .await (5 , TimeUnit .SECONDS )).isTrue ();
8170- assertThat (receivedStatus .get ().getCode ()).isEqualTo (Status .Code .UNAVAILABLE );
8375+ assertThat (receivedStatus .get ().getCode ()).isEqualTo (Status .Code .INTERNAL );
81718376 assertThat (receivedStatus .get ().getDescription ()).contains ("External processor stream failed" );
81728377 }
81738378
@@ -8351,7 +8556,7 @@ public void onClose(Status status, Metadata trailers) {
83518556
83528557 assertThat (sidecarLatch .await (5 , TimeUnit .SECONDS )).isTrue ();
83538558 assertThat (callCompletedLatch .await (5 , TimeUnit .SECONDS )).isTrue ();
8354- assertThat (receivedStatus .get ().getCode ()).isEqualTo (Status .Code .UNAVAILABLE );
8559+ assertThat (receivedStatus .get ().getCode ()).isEqualTo (Status .Code .INTERNAL );
83558560 assertThat (receivedStatus .get ().getDescription ()).contains ("External processor stream failed" );
83568561 }
83578562
@@ -8445,7 +8650,7 @@ public void onClose(Status status, Metadata trailers) {
84458650
84468651 assertThat (sidecarLatch .await (5 , TimeUnit .SECONDS )).isTrue ();
84478652 assertThat (callCompletedLatch .await (5 , TimeUnit .SECONDS )).isTrue ();
8448- assertThat (receivedStatus .get ().getCode ()).isEqualTo (Status .Code .UNAVAILABLE );
8653+ assertThat (receivedStatus .get ().getCode ()).isEqualTo (Status .Code .INTERNAL );
84498654 assertThat (receivedStatus .get ().getDescription ()).contains ("External processor stream failed" );
84508655 }
84518656
0 commit comments