17
17
package io .grpc .binder .internal ;
18
18
19
19
import static com .google .common .truth .Truth .assertThat ;
20
+ import static java .util .concurrent .TimeUnit .SECONDS ;
20
21
21
22
import android .content .Context ;
22
23
import android .os .DeadObjectException ;
23
24
import android .os .Parcel ;
24
25
import android .os .RemoteException ;
25
26
import androidx .test .core .app .ApplicationProvider ;
26
27
import androidx .test .ext .junit .runners .AndroidJUnit4 ;
27
- import com .google .common .util .concurrent .Futures ;
28
- import com .google .common .util .concurrent .ListenableFuture ;
29
28
import com .google .common .util .concurrent .SettableFuture ;
30
29
import com .google .errorprone .annotations .CanIgnoreReturnValue ;
31
30
import com .google .errorprone .annotations .concurrent .GuardedBy ;
39
38
import io .grpc .Status ;
40
39
import io .grpc .Status .Code ;
41
40
import io .grpc .binder .AndroidComponentAddress ;
42
- import io .grpc .binder .AsyncSecurityPolicy ;
43
41
import io .grpc .binder .BinderServerBuilder ;
44
42
import io .grpc .binder .HostServices ;
45
43
import io .grpc .binder .SecurityPolicy ;
46
44
import io .grpc .binder .internal .OneWayBinderProxies .BlackHoleOneWayBinderProxy ;
47
45
import io .grpc .binder .internal .OneWayBinderProxies .BlockingBinderDecorator ;
48
46
import io .grpc .binder .internal .OneWayBinderProxies .ThrowingOneWayBinderProxy ;
47
+ import io .grpc .binder .internal .SettableAsyncSecurityPolicy .AuthRequest ;
49
48
import io .grpc .internal .ClientStream ;
50
49
import io .grpc .internal .ClientStreamListener ;
51
50
import io .grpc .internal .ClientTransportFactory .ClientTransportOptions ;
62
61
import java .util .concurrent .ExecutorService ;
63
62
import java .util .concurrent .Executors ;
64
63
import java .util .concurrent .ScheduledExecutorService ;
65
- import java .util .concurrent .TimeUnit ;
66
64
import javax .annotation .Nullable ;
67
65
import org .junit .After ;
68
66
import org .junit .Before ;
@@ -101,7 +99,7 @@ public final class BinderClientTransportTest {
101
99
102
100
AndroidComponentAddress serverAddress ;
103
101
BinderTransport .BinderClientTransport transport ;
104
- SettableAsyncSecurityPolicy blockingSecurityPolicy = new SettableAsyncSecurityPolicy ();
102
+ BlockingSecurityPolicy blockingSecurityPolicy = new BlockingSecurityPolicy ();
105
103
106
104
private final ObjectPool <ScheduledExecutorService > executorServicePool =
107
105
new FixedObjectPool <>(Executors .newScheduledThreadPool (1 ));
@@ -172,6 +170,7 @@ public BinderClientTransportBuilder setReadyTimeoutMillis(int timeoutMillis) {
172
170
return this ;
173
171
}
174
172
173
+ @ CanIgnoreReturnValue
175
174
public BinderClientTransportBuilder setPreAuthorizeServer (boolean preAuthorizeServer ) {
176
175
factoryBuilder .setPreAuthorizeServers (preAuthorizeServer );
177
176
return this ;
@@ -196,7 +195,7 @@ public void tearDown() throws Exception {
196
195
private static void shutdownAndTerminate (ExecutorService executorService )
197
196
throws InterruptedException {
198
197
executorService .shutdownNow ();
199
- if (!executorService .awaitTermination (TIMEOUT_SECONDS , TimeUnit . SECONDS )) {
198
+ if (!executorService .awaitTermination (TIMEOUT_SECONDS , SECONDS )) {
200
199
throw new AssertionError ("executor failed to terminate promptly" );
201
200
}
202
201
}
@@ -292,16 +291,16 @@ public void testMessageProducerClosedAfterStream_b169313545() throws Exception {
292
291
@ Test
293
292
public void testNewStreamBeforeTransportReadyFails () throws Exception {
294
293
// Use a special SecurityPolicy that lets us act before the transport is setup/ready.
295
- SettableAsyncSecurityPolicy securityPolicy = new SettableAsyncSecurityPolicy ();
296
- transport = new BinderClientTransportBuilder ().setSecurityPolicy (securityPolicy ).build ();
294
+ transport =
295
+ new BinderClientTransportBuilder ().setSecurityPolicy (blockingSecurityPolicy ).build ();
297
296
transport .start (transportListener ).run ();
298
297
ClientStream stream =
299
298
transport .newStream (streamingMethodDesc , new Metadata (), CallOptions .DEFAULT , tracers );
300
299
stream .start (streamListener );
301
300
assertThat (streamListener .awaitClose ().getCode ()).isEqualTo (Code .INTERNAL );
302
301
303
302
// Unblock the SETUP_TRANSPORT handshake and make sure it becomes ready in the usual way.
304
- securityPolicy . setAuthorizationResult (Status .OK );
303
+ blockingSecurityPolicy . provideNextCheckAuthorizationResult (Status .OK );
305
304
transportListener .awaitReady ();
306
305
}
307
306
@@ -380,15 +379,21 @@ public void testBlackHoleEndpointConnectTimeout() throws Exception {
380
379
public void testBlackHoleSecurityPolicyAuthTimeout () throws Exception {
381
380
transport =
382
381
new BinderClientTransportBuilder ()
383
- .setSecurityPolicy (blockingSecurityPolicy )
384
382
.setPreAuthorizeServer (false )
383
+ .setSecurityPolicy (blockingSecurityPolicy )
385
384
.setReadyTimeoutMillis (1_234 )
386
385
.build ();
387
386
transport .start (transportListener ).run ();
387
+ // Take the next authRequest but don't respond to it, in order to trigger the ready timeout.
388
+ AuthRequest authRequest = securityPolicy .takeNextAuthRequest (TIMEOUT_SECONDS , SECONDS );
389
+
388
390
Status transportStatus = transportListener .awaitShutdown ();
389
391
assertThat (transportStatus .getCode ()).isEqualTo (Code .DEADLINE_EXCEEDED );
390
392
assertThat (transportStatus .getDescription ()).contains ("1234" );
391
393
transportListener .awaitTermination ();
394
+
395
+ // If the transport gave up waiting on auth, it should cancel its request.
396
+ assertThat (authRequest .isCancelled ()).isTrue ();
392
397
}
393
398
394
399
@ Test
@@ -432,8 +437,8 @@ public void testAsyncSecurityPolicyPreAuthFailure() throws Exception {
432
437
.setSecurityPolicy (securityPolicy )
433
438
.build ();
434
439
RuntimeException exception = new NullPointerException ();
435
- securityPolicy .setAuthorizationException (exception );
436
440
transport .start (transportListener ).run ();
441
+ securityPolicy .takeNextAuthRequest (TIMEOUT_SECONDS , SECONDS ).setResult (exception );
437
442
Status transportStatus = transportListener .awaitShutdown ();
438
443
assertThat (transportStatus .getCode ()).isEqualTo (Code .INTERNAL );
439
444
assertThat (transportStatus .getCause ()).isEqualTo (exception );
@@ -466,12 +471,45 @@ public void testAsyncSecurityPolicyPreAuthSuccess() throws Exception {
466
471
.build ();
467
472
securityPolicy .setAuthorizationResult (Status .PERMISSION_DENIED .withDescription ("xyzzy" ));
468
473
transport .start (transportListener ).run ();
474
+ securityPolicy
475
+ .takeNextAuthRequest (TIMEOUT_SECONDS , SECONDS )
476
+ .setResult (Status .PERMISSION_DENIED );
469
477
Status transportStatus = transportListener .awaitShutdown ();
470
478
assertThat (transportStatus .getCode ()).isEqualTo (Code .PERMISSION_DENIED );
471
479
assertThat (transportStatus .getDescription ()).contains ("xyzzy" );
472
480
transportListener .awaitTermination ();
473
481
}
474
482
483
+ @ Test
484
+ public void testAsyncSecurityPolicyAuthCancelledUponExternalTermination () throws Exception {
485
+ SettableAsyncSecurityPolicy securityPolicy = new SettableAsyncSecurityPolicy ();
486
+ transport = new BinderClientTransportBuilder ()
487
+ .setSecurityPolicy (securityPolicy )
488
+ .setPreAuthorizeServer (false )
489
+ .build ();
490
+ transport .start (transportListener ).run ();
491
+ AuthRequest authRequest = securityPolicy .takeNextAuthRequest (TIMEOUT_SECONDS , SECONDS );
492
+ transport .shutdownNow (Status .UNAVAILABLE ); // 'authRequest' remains unanswered!
493
+ transportListener .awaitShutdown ();
494
+ transportListener .awaitTermination ();
495
+ assertThat (authRequest .isCancelled ()).isTrue ();
496
+ }
497
+
498
+ @ Test
499
+ public void testAsyncSecurityPolicyPreAuthCancelledUponExternalTermination () throws Exception {
500
+ SettableAsyncSecurityPolicy securityPolicy = new SettableAsyncSecurityPolicy ();
501
+ transport = new BinderClientTransportBuilder ()
502
+ .setSecurityPolicy (securityPolicy )
503
+ .setPreAuthorizeServer (true )
504
+ .build ();
505
+ transport .start (transportListener ).run ();
506
+ AuthRequest preAuthRequest = securityPolicy .takeNextAuthRequest (TIMEOUT_SECONDS , SECONDS );
507
+ transport .shutdownNow (Status .UNAVAILABLE ); // 'authRequest' remains unanswered!
508
+ transportListener .awaitShutdown ();
509
+ transportListener .awaitTermination ();
510
+ assertThat (preAuthRequest .isCancelled ()).isTrue ();
511
+ }
512
+
475
513
private static void startAndAwaitReady (
476
514
BinderTransport .BinderClientTransport transport , TestTransportListener transportListener )
477
515
throws Exception {
@@ -493,7 +531,7 @@ public void transportShutdown(Status shutdownStatus) {
493
531
}
494
532
495
533
public Status awaitShutdown () throws Exception {
496
- return shutdownStatus .get (TIMEOUT_SECONDS , TimeUnit . SECONDS );
534
+ return shutdownStatus .get (TIMEOUT_SECONDS , SECONDS );
497
535
}
498
536
499
537
@ Override
@@ -504,7 +542,7 @@ public void transportTerminated() {
504
542
}
505
543
506
544
public void awaitTermination () throws Exception {
507
- isTerminated .get (TIMEOUT_SECONDS , TimeUnit . SECONDS );
545
+ isTerminated .get (TIMEOUT_SECONDS , SECONDS );
508
546
}
509
547
510
548
@ Override
@@ -515,7 +553,7 @@ public void transportReady() {
515
553
}
516
554
517
555
public void awaitReady () throws Exception {
518
- isReady .get (TIMEOUT_SECONDS , TimeUnit . SECONDS );
556
+ isReady .get (TIMEOUT_SECONDS , SECONDS );
519
557
}
520
558
521
559
@ Override
@@ -612,24 +650,23 @@ public synchronized void closed(Status status, RpcProgress rpcProgress, Metadata
612
650
}
613
651
}
614
652
615
- /** An AsyncSecurityPolicy that lets a test specify the outcome of checkAuthorizationAsync(). */
616
- static class SettableAsyncSecurityPolicy extends AsyncSecurityPolicy {
617
- private SettableFuture <Status > result = SettableFuture .create ();
653
+ /**
654
+ * A SecurityPolicy that blocks the transport authorization check until a test sets the outcome.
655
+ */
656
+ static class BlockingSecurityPolicy extends SecurityPolicy {
657
+ private final BlockingQueue <Status > results = new LinkedBlockingQueue <>();
618
658
619
- public void clearAuthorizationResult ( ) {
620
- result = SettableFuture . create ( );
659
+ public void provideNextCheckAuthorizationResult ( Status status ) {
660
+ results . add ( status );
621
661
}
622
662
623
- public boolean setAuthorizationResult (Status status ) {
624
- return result .set (status );
625
- }
626
-
627
- public boolean setAuthorizationException (Throwable t ) {
628
- return result .setException (t );
629
- }
630
-
631
- public ListenableFuture <Status > checkAuthorizationAsync (int uid ) {
632
- return Futures .nonCancellationPropagating (result );
663
+ @ Override
664
+ public Status checkAuthorization (int uid ) {
665
+ try {
666
+ return results .take ();
667
+ } catch (InterruptedException e ) {
668
+ return Status .fromThrowable (e );
669
+ }
633
670
}
634
671
}
635
672
}
0 commit comments