@@ -7748,9 +7748,9 @@ public void onClose(Status status, Metadata trailers) {
77487748 interceptCall(interceptor, METHOD_SAY_HELLO, callOptions, dataPlaneChannel);
77497749 proxyCall.start(appListener, new Metadata());
77507750
7751- // Verify application receives UNAVAILABLE due to sidecar failure
7751+ // Verify application receives INTERNAL due to sidecar failure
77527752 assertThat(closedLatch.await(5, TimeUnit.SECONDS)).isTrue();
7753- assertThat (closedStatus .get ().getCode ()).isEqualTo (Status .Code .UNAVAILABLE );
7753+ assertThat(closedStatus.get().getCode()).isEqualTo(Status.Code.INTERNAL );
77547754 assertThat(closedStatus.get().getDescription()).contains("External processor stream failed");
77557755
77567756 proxyCall.cancel("Cleanup", null);
@@ -8126,12 +8126,12 @@ public void onClose(Status status, Metadata trailers) {
81268126 proxyCall.sendMessage("test");
81278127 proxyCall.halfClose();
81288128
8129- // Verify application receives UNAVAILABLE with correct description
8129+ // Verify application receives INTERNAL with correct description
81308130 for (int i = 0; i < 10000 && closedLatch.getCount() > 0; i++) {
81318131 fakeClock.forwardTime(1, TimeUnit.MILLISECONDS);
81328132 }
81338133 assertThat(closedLatch.await(5, TimeUnit.SECONDS)).isTrue();
8134- assertThat (closedStatus .get ().getCode ()).isEqualTo (Status .Code .UNAVAILABLE );
8134+ assertThat(closedStatus.get().getCode()).isEqualTo(Status.Code.INTERNAL );
81358135 assertThat(closedStatus.get().getDescription()).contains("External processor stream failed");
81368136
81378137 proxyCall.cancel("Cleanup", null);
@@ -8271,9 +8271,9 @@ public void onCompleted() {
82718271 proxyCall.sendMessage("test");
82728272 proxyCall.halfClose();
82738273
8274- // Verify application receives UNAVAILABLE with correct description
8274+ // Verify application receives INTERNAL with correct description
82758275 assertThat(closedLatch.await(5, TimeUnit.SECONDS)).isTrue();
8276- assertThat (closedStatus .get ().getCode ()).isEqualTo (Status .Code .UNAVAILABLE );
8276+ assertThat(closedStatus.get().getCode()).isEqualTo(Status.Code.INTERNAL );
82778277 assertThat(closedStatus.get().getDescription()).contains("External processor stream failed");
82788278
82798279 proxyCall.cancel("Cleanup", null);
@@ -10623,9 +10623,9 @@ public void onCompleted() {
1062310623 assertThat(sidecarLatch.await(5, TimeUnit.SECONDS)).isTrue();
1062410624 assertThat(appCloseLatch.await(5, TimeUnit.SECONDS)).isTrue();
1062510625
10626- // The call should fail with UNAVAILABLE status
10626+ // The call should fail with INTERNAL status
1062710627 // due to stream failure triggered by protocol error
10628- assertThat (appStatus .get ().getCode ()).isEqualTo (Status .Code .UNAVAILABLE );
10628+ assertThat(appStatus.get().getCode()).isEqualTo(Status.Code.INTERNAL );
1062910629 assertThat(appStatus.get().getDescription()).contains("External processor stream failed");
1063010630
1063110631 channelManager.close();
@@ -11372,7 +11372,7 @@ public void onClose(Status status, Metadata trailers) {
1137211372 }
1137311373
1137411374 @Test
11375- public void givenFailureModeAllowTrue_whenExtProcStreamFailsDuringBodyOrEos_thenCallUnblocked ()
11375+ public void givenFailureModeAllowTrue_whenExtProcStreamFailsAfterRequestBodySent_thenCallFails ()
1137611376 throws Exception {
1137711377 String uniqueExtProcServerName = InProcessServerBuilder.generateName();
1137811378 String uniqueDataPlaneServerName = InProcessServerBuilder.generateName();
@@ -11455,10 +11455,12 @@ public void onCompleted() {}
1145511455 DEFAULT_CALL_OPTIONS.withExecutor(MoreExecutors.directExecutor()),
1145611456 dataPlaneChannel);
1145711457
11458+ final AtomicReference<Status> closedStatus = new AtomicReference<>();
1145811459 final CountDownLatch callCompletedLatch = new CountDownLatch(1);
1145911460 clientCall.start(new ClientCall.Listener<String>() {
1146011461 @Override
1146111462 public void onClose(Status status, Metadata trailers) {
11463+ closedStatus.set(status);
1146211464 callCompletedLatch.countDown();
1146311465 }
1146411466 }, new Metadata());
@@ -11474,9 +11476,137 @@ public void onClose(Status status, Metadata trailers) {
1147411476 responseObserverRef.get()
1147511477 .onError(new RuntimeException("Stream failure after sending body/EOS"));
1147611478
11477- // Verify that the call is unblocked (onClose is called) instead of hanging.
11479+ // Verify that the call failed with INTERNAL status instead of succeeding.
11480+ boolean completed = callCompletedLatch.await(5, TimeUnit.SECONDS);
11481+ assertThat(completed).isTrue();
11482+ assertThat(closedStatus.get().getCode()).isEqualTo(Status.Code.INTERNAL);
11483+ assertThat(closedStatus.get().getDescription()).contains("External processor stream failed");
11484+
11485+ channelManager.close();
11486+ }
11487+
11488+ @Test
11489+ public void givenFailureModeAllowTrue_whenExtProcStreamFailsAfterResponseBodySent_thenCallFails()
11490+ throws Exception {
11491+ String uniqueExtProcServerName = InProcessServerBuilder.generateName();
11492+ String uniqueDataPlaneServerName = InProcessServerBuilder.generateName();
11493+
11494+ ExternalProcessor proto = createBaseProto(uniqueExtProcServerName)
11495+ .setFailureModeAllow(true)
11496+ .setProcessingMode(ProcessingMode.newBuilder()
11497+ .setRequestHeaderMode(ProcessingMode.HeaderSendMode.SEND)
11498+ .setRequestBodyMode(ProcessingMode.BodySendMode.NONE)
11499+ .setResponseHeaderMode(ProcessingMode.HeaderSendMode.SEND)
11500+ .setResponseBodyMode(ProcessingMode.BodySendMode.GRPC)
11501+ .setResponseTrailerMode(ProcessingMode.HeaderSendMode.SEND)
11502+ .build())
11503+ .build();
11504+ ConfigOrError<ExternalProcessorFilterConfig> configOrError =
11505+ provider.parseFilterConfig(Any.pack(proto), filterContext);
11506+ assertThat(configOrError.errorDetail).isNull();
11507+ ExternalProcessorFilterConfig filterConfig = configOrError.config;
11508+
11509+ final AtomicReference<StreamObserver<ProcessingResponse>> responseObserverRef =
11510+ new AtomicReference<>();
11511+ final CountDownLatch streamActiveLatch = new CountDownLatch(1);
11512+ final CountDownLatch streamFailedLatch = new CountDownLatch(1);
11513+
11514+ ExternalProcessorGrpc.ExternalProcessorImplBase extProcImpl =
11515+ new ExternalProcessorGrpc.ExternalProcessorImplBase() {
11516+ @Override
11517+ public StreamObserver<ProcessingRequest> process(
11518+ StreamObserver<ProcessingResponse> responseObserver) {
11519+ responseObserverRef.set(responseObserver);
11520+ streamActiveLatch.countDown();
11521+ return new StreamObserver<ProcessingRequest>() {
11522+ @Override
11523+ public void onNext(ProcessingRequest request) {
11524+ if (request.hasRequestHeaders()) {
11525+ responseObserver.onNext(ProcessingResponse.newBuilder()
11526+ .setRequestHeaders(HeadersResponse.newBuilder().build())
11527+ .build());
11528+ } else if (request.hasResponseHeaders()) {
11529+ responseObserver.onNext(ProcessingResponse.newBuilder()
11530+ .setResponseHeaders(HeadersResponse.newBuilder().build())
11531+ .build());
11532+ } else if (request.hasResponseBody()) {
11533+ // Fail the stream once we see response body message
11534+ responseObserver.onError(
11535+ Status.INTERNAL.withDescription("Stream failure after response body")
11536+ .asRuntimeException());
11537+ streamFailedLatch.countDown();
11538+ }
11539+ }
11540+
11541+ @Override
11542+ public void onError(Throwable t) {}
11543+
11544+ @Override
11545+ public void onCompleted() {}
11546+ };
11547+ }
11548+ };
11549+
11550+ grpcCleanup.register(InProcessServerBuilder.forName(uniqueExtProcServerName)
11551+ .addService(extProcImpl)
11552+ .directExecutor()
11553+ .build().start());
11554+
11555+ CachedChannelManager channelManager = new CachedChannelManager(config -> {
11556+ return grpcCleanup.register(
11557+ InProcessChannelBuilder.forName(uniqueExtProcServerName)
11558+ .directExecutor()
11559+ .build());
11560+ });
11561+
11562+ ExternalProcessorClientInterceptor interceptor = new ExternalProcessorClientInterceptor(
11563+ filterConfig, channelManager, scheduler, FAKE_CONTEXT);
11564+
11565+ MutableHandlerRegistry dataPlaneRegistry = new MutableHandlerRegistry();
11566+ grpcCleanup.register(InProcessServerBuilder.forName(uniqueDataPlaneServerName)
11567+ .fallbackHandlerRegistry(dataPlaneRegistry)
11568+ .directExecutor()
11569+ .build().start());
11570+
11571+ dataPlaneRegistry.addService(ServerServiceDefinition.builder("test.TestService")
11572+ .addMethod(METHOD_SAY_HELLO, ServerCalls.asyncUnaryCall((request, responseObserver) -> {
11573+ responseObserver.onNext("response-body-msg");
11574+ responseObserver.onCompleted();
11575+ })).build());
11576+
11577+ ManagedChannel dataPlaneChannel = grpcCleanup.register(
11578+ InProcessChannelBuilder.forName(uniqueDataPlaneServerName)
11579+ .directExecutor()
11580+ .build());
11581+
11582+ ClientCall<String, String> clientCall = interceptCall(
11583+ interceptor, METHOD_SAY_HELLO,
11584+ DEFAULT_CALL_OPTIONS.withExecutor(MoreExecutors.directExecutor()),
11585+ dataPlaneChannel);
11586+
11587+ final AtomicReference<Status> closedStatus = new AtomicReference<>();
11588+ final CountDownLatch callCompletedLatch = new CountDownLatch(1);
11589+ clientCall.start(new ClientCall.Listener<String>() {
11590+ @Override
11591+ public void onClose(Status status, Metadata trailers) {
11592+ closedStatus.set(status);
11593+ callCompletedLatch.countDown();
11594+ }
11595+ }, new Metadata());
11596+ clientCall.request(1);
11597+
11598+ boolean active = streamActiveLatch.await(5, TimeUnit.SECONDS);
11599+ assertThat(active).isTrue();
11600+
11601+ // Since request body mode is NONE, this sendMessage is NOT sent to ext_proc
11602+ clientCall.sendMessage("app-msg");
11603+ clientCall.halfClose();
11604+
11605+ // Verify call completed and failed with INTERNAL status
1147811606 boolean completed = callCompletedLatch.await(5, TimeUnit.SECONDS);
1147911607 assertThat(completed).isTrue();
11608+ assertThat(closedStatus.get().getCode()).isEqualTo(Status.Code.INTERNAL);
11609+ assertThat(closedStatus.get().getDescription()).contains("External processor stream failed");
1148011610
1148111611 channelManager.close();
1148211612 }
0 commit comments