@@ -197,6 +197,59 @@ protected void onException(Throwable throwable) {
197197 .contains ("ProtocolNegotiators.ClientTlsHandler" );
198198 CommonCertProviderTestUtils .register0 ();
199199 }
200+
201+ @ Test
202+ public void clientSecurityHandler_addLast ()
203+ throws InterruptedException , TimeoutException , ExecutionException {
204+ FakeClock executor = new FakeClock ();
205+ CommonCertProviderTestUtils .register (executor );
206+ Bootstrapper .BootstrapInfo bootstrapInfoForClient = CommonBootstrapperTestUtils
207+ .buildBootstrapInfo ("google_cloud_private_spiffe-client" , CLIENT_KEY_FILE , CLIENT_PEM_FILE ,
208+ CA_PEM_FILE , null , null , null , null , null );
209+ UpstreamTlsContext upstreamTlsContext =
210+ CommonTlsContextTestsUtil
211+ .buildUpstreamTlsContext ("google_cloud_private_spiffe-client" , true , null , false );
212+
213+ SslContextProviderSupplier sslContextProviderSupplier =
214+ new SslContextProviderSupplier (upstreamTlsContext ,
215+ new TlsContextManagerImpl (bootstrapInfoForClient ));
216+ ClientSecurityHandler clientSecurityHandler =
217+ new ClientSecurityHandler (grpcHandler , sslContextProviderSupplier , HOSTNAME );
218+ pipeline .addLast (clientSecurityHandler );
219+ channelHandlerCtx = pipeline .context (clientSecurityHandler );
220+ assertNotNull (channelHandlerCtx );
221+
222+ // kick off protocol negotiation.
223+ pipeline .fireUserEventTriggered (InternalProtocolNegotiationEvent .getDefault ());
224+ final SettableFuture <Object > future = SettableFuture .create ();
225+ sslContextProviderSupplier
226+ .updateSslContext (new SslContextProvider .Callback (MoreExecutors .directExecutor ()) {
227+ @ Override
228+ public void updateSslContext (SslContext sslContext ) {
229+ future .set (sslContext );
230+ }
231+
232+ @ Override
233+ protected void onException (Throwable throwable ) {
234+ future .set (throwable );
235+ }
236+ }, null );
237+ assertThat (executor .runDueTasks ()).isEqualTo (1 );
238+ channel .runPendingTasks ();
239+ Object fromFuture = future .get (2 , TimeUnit .SECONDS );
240+ assertThat (fromFuture ).isInstanceOf (SslContext .class );
241+ channel .runPendingTasks ();
242+ channelHandlerCtx = pipeline .context (clientSecurityHandler );
243+ assertThat (channelHandlerCtx ).isNull ();
244+
245+ // pipeline should have SslHandler and ClientTlsHandler
246+ Iterator <Map .Entry <String , ChannelHandler >> iterator = pipeline .iterator ();
247+ assertThat (iterator .next ().getValue ()).isInstanceOf (SslHandler .class );
248+ // ProtocolNegotiators.ClientTlsHandler.class not accessible, get canonical name
249+ assertThat (iterator .next ().getValue ().getClass ().getCanonicalName ())
250+ .contains ("ProtocolNegotiators.ClientTlsHandler" );
251+ CommonCertProviderTestUtils .register0 ();
252+ }
200253
201254 @ Test
202255 public void sniInClientSecurityHandler_autoHostSniIsTrue_usesEndpointHostname () {
0 commit comments