@@ -625,6 +625,73 @@ public HeaderMutations responseHeaderMutations() {
625625 observer .onNext (CheckResponse .getDefaultInstance ());
626626 }
627627
628+ @ Test
629+ public void allow_whenDelayedCallCancelledInFlight_setCallReturnsNull () {
630+ doAnswer (invocation -> {
631+ StreamObserver <CheckResponse > obs = invocation .getArgument (1 );
632+ obs .onNext (CheckResponse .newBuilder ()
633+ .setStatus (com .google .rpc .Status .newBuilder ().setCode (0 ).build ())
634+ .setOkResponse (OkHttpResponse .getDefaultInstance ())
635+ .build ());
636+ obs .onCompleted ();
637+ return null ;
638+ }).when (authzService ).check (any (), any ());
639+
640+ TestDelayedCall <SimpleRequest , SimpleResponse > delayedCall =
641+ new TestDelayedCall <>(MoreExecutors .directExecutor (), scheduler , null );
642+ CapturingListener <SimpleResponse > listener = new CapturingListener <>();
643+ delayedCall .start (listener , new Metadata ());
644+ delayedCall .cancel ("cancelled in flight" , null );
645+
646+ Context .CancellableContext authzCtx = Context .current ().withCancellation ();
647+ AuthzCallbackObserver <SimpleRequest , SimpleResponse > observer =
648+ new AuthzCallbackObserver <>(
649+ delayedCall , channel ,
650+ SimpleServiceGrpc .getUnaryRpcMethod (),
651+ CallOptions .DEFAULT ,
652+ MoreExecutors .directExecutor (),
653+ responseHandler , failClosedConfig (), authzCtx );
654+
655+ authzCtx .run (() -> {
656+ AuthorizationGrpc .newStub (channel )
657+ .check (CheckRequest .getDefaultInstance (), observer );
658+ });
659+
660+ assertThat (capturedBackendHeaders ).isNull ();
661+ assertThat (listener .getCloseStatus ().getCode ()).isEqualTo (Status .Code .CANCELLED );
662+ }
663+
664+ @ Test
665+ public void allow_whenDelayedCallNotStarted_setCallReturnsNull () {
666+ doAnswer (invocation -> {
667+ StreamObserver <CheckResponse > obs = invocation .getArgument (1 );
668+ obs .onNext (CheckResponse .newBuilder ()
669+ .setStatus (com .google .rpc .Status .newBuilder ().setCode (0 ).build ())
670+ .setOkResponse (OkHttpResponse .getDefaultInstance ())
671+ .build ());
672+ obs .onCompleted ();
673+ return null ;
674+ }).when (authzService ).check (any (), any ());
675+
676+ TestDelayedCall <SimpleRequest , SimpleResponse > delayedCall =
677+ new TestDelayedCall <>(MoreExecutors .directExecutor (), scheduler , null );
678+ Context .CancellableContext authzCtx = Context .current ().withCancellation ();
679+ AuthzCallbackObserver <SimpleRequest , SimpleResponse > observer =
680+ new AuthzCallbackObserver <>(
681+ delayedCall , channel ,
682+ SimpleServiceGrpc .getUnaryRpcMethod (),
683+ CallOptions .DEFAULT ,
684+ MoreExecutors .directExecutor (),
685+ responseHandler , failClosedConfig (), authzCtx );
686+
687+ authzCtx .run (() -> {
688+ AuthorizationGrpc .newStub (channel )
689+ .check (CheckRequest .getDefaultInstance (), observer );
690+ });
691+
692+ assertThat (capturedBackendHeaders ).isNull ();
693+ }
694+
628695
629696 private static final class TestDelayedCall <ReqT , RespT >
630697 extends DelayedClientCall <ReqT , RespT > {
0 commit comments