|  | @@ -19,6 +19,7 @@
 | 
	
		
			
				|  |  |  using System;
 | 
	
		
			
				|  |  |  using System.Collections.Generic;
 | 
	
		
			
				|  |  |  using System.Runtime.InteropServices;
 | 
	
		
			
				|  |  | +using System.Runtime.CompilerServices;
 | 
	
		
			
				|  |  |  using Grpc.Core.Utils;
 | 
	
		
			
				|  |  |  using Grpc.Core.Logging;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -31,6 +32,12 @@ namespace Grpc.Core.Internal
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  |          static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<DefaultCallCredentialsConfigurator>();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        // Native credentials object need to be kept alive once initialized for subchannel sharing to work correctly
 | 
	
		
			
				|  |  | +        // with secure connections. See https://github.com/grpc/grpc/issues/15207.
 | 
	
		
			
				|  |  | +        // We rely on finalizer to clean up the native portion of ChannelCredentialsSafeHandle after the ChannelCredentials
 | 
	
		
			
				|  |  | +        // instance becomes unused.
 | 
	
		
			
				|  |  | +        static readonly ConditionalWeakTable<ChannelCredentials, Lazy<ChannelCredentialsSafeHandle>> CachedNativeCredentials = new ConditionalWeakTable<ChannelCredentials, Lazy<ChannelCredentialsSafeHandle>>();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          bool configured;
 | 
	
		
			
				|  |  |          ChannelCredentialsSafeHandle nativeCredentials;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -48,18 +55,30 @@ namespace Grpc.Core.Internal
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              GrpcPreconditions.CheckState(!configured);
 | 
	
		
			
				|  |  |              configured = true;
 | 
	
		
			
				|  |  | +            nativeCredentials = GetOrCreateNativeCredentials((ChannelCredentials) state,
 | 
	
		
			
				|  |  | +                () => CreateNativeSslCredentials(rootCertificates, keyCertificatePair, verifyPeerCallback));
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public override void SetCompositeCredentials(object state, ChannelCredentials channelCredentials, CallCredentials callCredentials)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            GrpcPreconditions.CheckState(!configured);
 | 
	
		
			
				|  |  | +            configured = true;
 | 
	
		
			
				|  |  | +            nativeCredentials = GetOrCreateNativeCredentials((ChannelCredentials) state,
 | 
	
		
			
				|  |  | +                () => CreateNativeCompositeCredentials(channelCredentials, callCredentials));
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private ChannelCredentialsSafeHandle CreateNativeSslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair, VerifyPeerCallback verifyPeerCallback)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  |              IntPtr verifyPeerCallbackTag = IntPtr.Zero;
 | 
	
		
			
				|  |  |              if (verifyPeerCallback != null)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  verifyPeerCallbackTag = new VerifyPeerCallbackRegistration(verifyPeerCallback).CallbackRegistration.Tag;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -            nativeCredentials = ChannelCredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair, verifyPeerCallbackTag);
 | 
	
		
			
				|  |  | +            return ChannelCredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair, verifyPeerCallbackTag);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        public override void SetCompositeCredentials(object state, ChannelCredentials channelCredentials, CallCredentials callCredentials)
 | 
	
		
			
				|  |  | +        private ChannelCredentialsSafeHandle CreateNativeCompositeCredentials(ChannelCredentials channelCredentials, CallCredentials callCredentials)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            GrpcPreconditions.CheckState(!configured);
 | 
	
		
			
				|  |  | -            configured = true;
 | 
	
		
			
				|  |  |              using (var callCreds = callCredentials.ToNativeCredentials())
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  var nativeComposite = ChannelCredentialsSafeHandle.CreateComposite(channelCredentials.ToNativeCredentials(), callCreds);
 | 
	
	
		
			
				|  | @@ -67,8 +86,32 @@ namespace Grpc.Core.Internal
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  |                      throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials.");
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | -                nativeCredentials = nativeComposite;
 | 
	
		
			
				|  |  | +                return nativeComposite;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private ChannelCredentialsSafeHandle GetOrCreateNativeCredentials(ChannelCredentials key, Func<ChannelCredentialsSafeHandle> nativeCredentialsFactory)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            Lazy<ChannelCredentialsSafeHandle> lazyValue;
 | 
	
		
			
				|  |  | +            while (true)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                if (CachedNativeCredentials.TryGetValue(key, out lazyValue))
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    break;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                lazyValue = new Lazy<ChannelCredentialsSafeHandle>(nativeCredentialsFactory);
 | 
	
		
			
				|  |  | +                try
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    CachedNativeCredentials.Add(key, lazyValue);
 | 
	
		
			
				|  |  | +                    break;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                catch (ArgumentException)
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    // key exists, next TryGetValue should fetch the value
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | +            return lazyValue.Value;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          private class VerifyPeerCallbackRegistration
 |