diff --git a/src/csharp/Grpc.Core.Api/CallCredentials.cs b/src/csharp/Grpc.Core.Api/CallCredentials.cs
index 6344a881070..3b150a211c2 100644
--- a/src/csharp/Grpc.Core.Api/CallCredentials.cs
+++ b/src/csharp/Grpc.Core.Api/CallCredentials.cs
@@ -51,8 +51,8 @@ namespace Grpc.Core
}
///
- /// Populates this call credential instances.
- /// You never need to invoke this, part of internal implementation.
+ /// Populates call credentials configurator with this instance's configuration.
+ /// End users never need to invoke this method as it is part of internal implementation.
///
public abstract void InternalPopulateConfiguration(CallCredentialsConfiguratorBase configurator, object state);
diff --git a/src/csharp/Grpc.Core/ChannelCredentials.cs b/src/csharp/Grpc.Core.Api/ChannelCredentials.cs
similarity index 64%
rename from src/csharp/Grpc.Core/ChannelCredentials.cs
rename to src/csharp/Grpc.Core.Api/ChannelCredentials.cs
index dcbe9f3599b..10020a3d8d4 100644
--- a/src/csharp/Grpc.Core/ChannelCredentials.cs
+++ b/src/csharp/Grpc.Core.Api/ChannelCredentials.cs
@@ -18,11 +18,9 @@
using System;
using System.Collections.Generic;
-using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Grpc.Core.Internal;
-using Grpc.Core.Logging;
using Grpc.Core.Utils;
namespace Grpc.Core
@@ -33,7 +31,9 @@ namespace Grpc.Core
public abstract class ChannelCredentials
{
static readonly ChannelCredentials InsecureInstance = new InsecureCredentialsImpl();
- readonly Lazy cachedNativeCredentials;
+
+ // TODO: caching the instance!!!!
+ //readonly Lazy cachedNativeCredentials;
///
/// Creates a new instance of channel credentials
@@ -44,7 +44,7 @@ namespace Grpc.Core
// 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.
- this.cachedNativeCredentials = new Lazy(() => CreateNativeCredentials());
+ //this.cachedNativeCredentials = new Lazy(() => CreateNativeCredentials());
}
///
@@ -72,36 +72,39 @@ namespace Grpc.Core
}
///
- /// Gets native object for the credentials, creating one if it already doesn't exist. May return null if insecure channel
- /// should be created. Caller must not call Dispose() on the returned native credentials as their lifetime
- /// is managed by this class (and instances of native credentials are cached).
+ /// Populates channel credentials configurator with this instance's configuration.
+ /// End users never need to invoke this method as it is part of internal implementation.
///
- /// The native credentials.
- internal ChannelCredentialsSafeHandle GetNativeCredentials()
- {
- return cachedNativeCredentials.Value;
- }
-
- ///
- /// Creates a new native object for the credentials. May return null if insecure channel
- /// should be created. For internal use only, use instead.
- ///
- /// The native credentials.
- internal abstract ChannelCredentialsSafeHandle CreateNativeCredentials();
+ public abstract void InternalPopulateConfiguration(ChannelCredentialsConfiguratorBase configurator, object state);
+
+ // /
+ // / Gets native object for the credentials, creating one if it already doesn't exist. May return null if insecure channel
+ // / should be created. Caller must not call Dispose() on the returned native credentials as their lifetime
+ // / is managed by this class (and instances of native credentials are cached).
+ // /
+ // / The native credentials.
+ //internal ChannelCredentialsSafeHandle GetNativeCredentials()
+ //{
+ // return cachedNativeCredentials.Value;
+ //}
+
+ // /
+ // / Creates a new native object for the credentials. May return null if insecure channel
+ // / should be created. For internal use only, use instead.
+ // /
+ // / The native credentials.
+ //internal abstract ChannelCredentialsSafeHandle CreateNativeCredentials();
///
/// Returns true if this credential type allows being composed by CompositeCredentials.
///
- internal virtual bool IsComposable
- {
- get { return false; }
- }
+ internal virtual bool IsComposable => false;
private sealed class InsecureCredentialsImpl : ChannelCredentials
{
- internal override ChannelCredentialsSafeHandle CreateNativeCredentials()
+ public override void InternalPopulateConfiguration(ChannelCredentialsConfiguratorBase configurator, object state)
{
- return null;
+ configurator.SetInsecureCredentials(state);
}
}
}
@@ -126,8 +129,6 @@ namespace Grpc.Core
///
public sealed class SslCredentials : ChannelCredentials
{
- static readonly ILogger Logger = GrpcEnvironment.Logger.ForType();
-
readonly string rootCertificates;
readonly KeyCertificatePair keyCertificatePair;
readonly VerifyPeerCallback verifyPeerCallback;
@@ -196,63 +197,16 @@ namespace Grpc.Core
}
}
- // Composing composite makes no sense.
- internal override bool IsComposable
- {
- get { return true; }
- }
-
- internal override ChannelCredentialsSafeHandle CreateNativeCredentials()
+ ///
+ /// Populates channel credentials configurator with this instance's configuration.
+ /// End users never need to invoke this method as it is part of internal implementation.
+ ///
+ public override void InternalPopulateConfiguration(ChannelCredentialsConfiguratorBase configurator, object state)
{
- IntPtr verifyPeerCallbackTag = IntPtr.Zero;
- if (verifyPeerCallback != null)
- {
- verifyPeerCallbackTag = new VerifyPeerCallbackRegistration(verifyPeerCallback).CallbackRegistration.Tag;
- }
- return ChannelCredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair, verifyPeerCallbackTag);
+ configurator.SetSslCredentials(state, rootCertificates, keyCertificatePair, verifyPeerCallback);
}
- private class VerifyPeerCallbackRegistration
- {
- readonly VerifyPeerCallback verifyPeerCallback;
- readonly NativeCallbackRegistration callbackRegistration;
-
- public VerifyPeerCallbackRegistration(VerifyPeerCallback verifyPeerCallback)
- {
- this.verifyPeerCallback = verifyPeerCallback;
- this.callbackRegistration = NativeCallbackDispatcher.RegisterCallback(HandleUniversalCallback);
- }
-
- public NativeCallbackRegistration CallbackRegistration => callbackRegistration;
-
- private int HandleUniversalCallback(IntPtr arg0, IntPtr arg1, IntPtr arg2, IntPtr arg3, IntPtr arg4, IntPtr arg5)
- {
- return VerifyPeerCallbackHandler(arg0, arg1, arg2 != IntPtr.Zero);
- }
-
- private int VerifyPeerCallbackHandler(IntPtr targetName, IntPtr peerPem, bool isDestroy)
- {
- if (isDestroy)
- {
- this.callbackRegistration.Dispose();
- return 0;
- }
-
- try
- {
- var context = new VerifyPeerContext(Marshal.PtrToStringAnsi(targetName), Marshal.PtrToStringAnsi(peerPem));
-
- return this.verifyPeerCallback(context) ? 0 : 1;
- }
- catch (Exception e)
- {
- // eat the exception, we must not throw when inside callback from native code.
- Logger.Error(e, "Exception occurred while invoking verify peer callback handler.");
- // Return validation failure in case of exception.
- return 1;
- }
- }
- }
+ internal override bool IsComposable => true;
}
///
@@ -277,17 +231,9 @@ namespace Grpc.Core
GrpcPreconditions.CheckArgument(channelCredentials.IsComposable, "Supplied channel credentials do not allow composition.");
}
- internal override ChannelCredentialsSafeHandle CreateNativeCredentials()
+ public override void InternalPopulateConfiguration(ChannelCredentialsConfiguratorBase configurator, object state)
{
- using (var callCreds = callCredentials.ToNativeCredentials())
- {
- var nativeComposite = ChannelCredentialsSafeHandle.CreateComposite(channelCredentials.GetNativeCredentials(), callCreds);
- if (nativeComposite.IsInvalid)
- {
- throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials.");
- }
- return nativeComposite;
- }
+ configurator.SetCompositeCredentials(state, channelCredentials, callCredentials);
}
}
}
diff --git a/src/csharp/Grpc.Core.Api/ChannelCredentialsConfiguratorBase.cs b/src/csharp/Grpc.Core.Api/ChannelCredentialsConfiguratorBase.cs
new file mode 100644
index 00000000000..5c779bc66b4
--- /dev/null
+++ b/src/csharp/Grpc.Core.Api/ChannelCredentialsConfiguratorBase.cs
@@ -0,0 +1,44 @@
+#region Copyright notice and license
+
+// Copyright 2019 The gRPC Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#endregion
+
+using System.Collections.Generic;
+
+namespace Grpc.Core
+{
+ ///
+ /// Base class for objects that can consume configuration from CallCredentials objects.
+ /// Note: experimental API that can change or be removed without any prior notice.
+ ///
+ public abstract class ChannelCredentialsConfiguratorBase
+ {
+ ///
+ /// Configures the credentials to use insecure credentials.
+ ///
+ public abstract void SetInsecureCredentials(object state);
+
+ ///
+ /// Configures the credentials to use SslCredentials.
+ ///
+ public abstract void SetSslCredentials(object state, string rootCertificates, KeyCertificatePair keyCertificatePair, VerifyPeerCallback verifyPeerCallback);
+
+ ///
+ /// Configures the credentials to use composite channel credentials (a composite of channel credentials and call credentials).
+ ///
+ public abstract void SetCompositeCredentials(object state, ChannelCredentials channelCredentials, CallCredentials callCredentials);
+ }
+}
diff --git a/src/csharp/Grpc.Core/KeyCertificatePair.cs b/src/csharp/Grpc.Core.Api/KeyCertificatePair.cs
similarity index 100%
rename from src/csharp/Grpc.Core/KeyCertificatePair.cs
rename to src/csharp/Grpc.Core.Api/KeyCertificatePair.cs
diff --git a/src/csharp/Grpc.Core/VerifyPeerContext.cs b/src/csharp/Grpc.Core.Api/VerifyPeerContext.cs
similarity index 100%
rename from src/csharp/Grpc.Core/VerifyPeerContext.cs
rename to src/csharp/Grpc.Core.Api/VerifyPeerContext.cs
diff --git a/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs b/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs
index 843d88bfb6a..fd50ce4f064 100644
--- a/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs
+++ b/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs
@@ -48,25 +48,25 @@ namespace Grpc.Core.Tests
{
// always returning the same native object is critical for subchannel sharing to work with secure channels
var creds = new SslCredentials();
- var nativeCreds1 = creds.GetNativeCredentials();
- var nativeCreds2 = creds.GetNativeCredentials();
+ var nativeCreds1 = creds.ToNativeCredentials();
+ var nativeCreds2 = creds.ToNativeCredentials();
Assert.AreSame(nativeCreds1, nativeCreds2);
}
[Test]
public void ChannelCredentials_CreateExceptionIsCached()
{
- var creds = new ChannelCredentialsWithCreateNativeThrows();
- var ex1 = Assert.Throws(typeof(Exception), () => creds.GetNativeCredentials());
- var ex2 = Assert.Throws(typeof(Exception), () => creds.GetNativeCredentials());
+ var creds = new ChannelCredentialsWithPopulateConfigurationThrows();
+ var ex1 = Assert.Throws(typeof(Exception), () => creds.ToNativeCredentials());
+ var ex2 = Assert.Throws(typeof(Exception), () => creds.ToNativeCredentials());
Assert.AreSame(ex1, ex2);
}
- internal class ChannelCredentialsWithCreateNativeThrows : ChannelCredentials
+ internal class ChannelCredentialsWithPopulateConfigurationThrows : ChannelCredentials
{
internal override bool IsComposable => false;
- internal override ChannelCredentialsSafeHandle CreateNativeCredentials()
+ public override void InternalPopulateConfiguration(ChannelCredentialsConfiguratorBase configurator, object state)
{
throw new Exception("Creation of native credentials has failed on purpose.");
}
diff --git a/src/csharp/Grpc.Core.Tests/FakeCredentials.cs b/src/csharp/Grpc.Core.Tests/FakeCredentials.cs
index 59587b9a510..e38e0e136cc 100644
--- a/src/csharp/Grpc.Core.Tests/FakeCredentials.cs
+++ b/src/csharp/Grpc.Core.Tests/FakeCredentials.cs
@@ -34,9 +34,9 @@ namespace Grpc.Core.Tests
get { return composable; }
}
- internal override ChannelCredentialsSafeHandle CreateNativeCredentials()
+ public override void InternalPopulateConfiguration(ChannelCredentialsConfiguratorBase configurator, object state)
{
- return null;
+ // not invoking configuration on purpose
}
}
diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs
index fb58c077c70..8d1fb921ff4 100644
--- a/src/csharp/Grpc.Core/Channel.cs
+++ b/src/csharp/Grpc.Core/Channel.cs
@@ -72,7 +72,7 @@ namespace Grpc.Core
this.completionQueue = this.environment.PickCompletionQueue();
using (var nativeChannelArgs = ChannelOptions.CreateChannelArgs(this.options.Values))
{
- var nativeCredentials = credentials.GetNativeCredentials();
+ var nativeCredentials = credentials.ToNativeCredentials();
if (nativeCredentials != null)
{
this.handle = ChannelSafeHandle.CreateSecure(nativeCredentials, target, nativeChannelArgs);
diff --git a/src/csharp/Grpc.Core/ForwardedTypes.cs b/src/csharp/Grpc.Core/ForwardedTypes.cs
index ad30814eb70..f860545a9a2 100644
--- a/src/csharp/Grpc.Core/ForwardedTypes.cs
+++ b/src/csharp/Grpc.Core/ForwardedTypes.cs
@@ -40,6 +40,7 @@ using Grpc.Core.Utils;
[assembly:TypeForwardedToAttribute(typeof(CallOptions))]
[assembly:TypeForwardedToAttribute(typeof(ClientBase))]
[assembly:TypeForwardedToAttribute(typeof(ClientBase<>))]
+[assembly:TypeForwardedToAttribute(typeof(ChannelCredentials))]
[assembly:TypeForwardedToAttribute(typeof(ClientInterceptorContext<,>))]
[assembly:TypeForwardedToAttribute(typeof(ContextPropagationOptions))]
[assembly:TypeForwardedToAttribute(typeof(ContextPropagationToken))]
@@ -50,6 +51,7 @@ using Grpc.Core.Utils;
[assembly:TypeForwardedToAttribute(typeof(Interceptor))]
[assembly:TypeForwardedToAttribute(typeof(InterceptingCallInvoker))]
[assembly:TypeForwardedToAttribute(typeof(IServerStreamWriter<>))]
+[assembly:TypeForwardedToAttribute(typeof(KeyCertificatePair))]
[assembly:TypeForwardedToAttribute(typeof(Marshaller<>))]
[assembly:TypeForwardedToAttribute(typeof(Marshallers))]
[assembly:TypeForwardedToAttribute(typeof(Metadata))]
@@ -65,8 +67,10 @@ using Grpc.Core.Utils;
[assembly:TypeForwardedToAttribute(typeof(DuplexStreamingServerMethod<,>))]
[assembly:TypeForwardedToAttribute(typeof(ServerServiceDefinition))]
[assembly:TypeForwardedToAttribute(typeof(ServiceBinderBase))]
+[assembly:TypeForwardedToAttribute(typeof(SslCredentials))]
[assembly:TypeForwardedToAttribute(typeof(Status))]
[assembly:TypeForwardedToAttribute(typeof(StatusCode))]
+[assembly:TypeForwardedToAttribute(typeof(VerifyPeerContext))]
[assembly:TypeForwardedToAttribute(typeof(VersionInfo))]
[assembly:TypeForwardedToAttribute(typeof(WriteOptions))]
[assembly:TypeForwardedToAttribute(typeof(WriteFlags))]
diff --git a/src/csharp/Grpc.Core/Internal/DefaultChannelCredentialsConfigurator.cs b/src/csharp/Grpc.Core/Internal/DefaultChannelCredentialsConfigurator.cs
new file mode 100644
index 00000000000..8b9e1758598
--- /dev/null
+++ b/src/csharp/Grpc.Core/Internal/DefaultChannelCredentialsConfigurator.cs
@@ -0,0 +1,130 @@
+#region Copyright notice and license
+
+// Copyright 2019 The gRPC Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using Grpc.Core.Utils;
+using Grpc.Core.Logging;
+
+namespace Grpc.Core.Internal
+{
+ ///
+ /// Creates native call credential objects from instances of ChannelCredentials.
+ ///
+ internal class DefaultChannelCredentialsConfigurator : ChannelCredentialsConfiguratorBase
+ {
+ static readonly ILogger Logger = GrpcEnvironment.Logger.ForType();
+
+ bool configured;
+ ChannelCredentialsSafeHandle nativeCredentials;
+
+ public ChannelCredentialsSafeHandle NativeCredentials => nativeCredentials;
+
+ public override void SetInsecureCredentials(object state)
+ {
+ GrpcPreconditions.CheckState(!configured);
+ // null corresponds to insecure credentials.
+ configured = true;
+ nativeCredentials = null;
+ }
+
+ public override void SetSslCredentials(object state, string rootCertificates, KeyCertificatePair keyCertificatePair, VerifyPeerCallback verifyPeerCallback)
+ {
+ GrpcPreconditions.CheckState(!configured);
+ configured = true;
+ IntPtr verifyPeerCallbackTag = IntPtr.Zero;
+ if (verifyPeerCallback != null)
+ {
+ verifyPeerCallbackTag = new VerifyPeerCallbackRegistration(verifyPeerCallback).CallbackRegistration.Tag;
+ }
+ nativeCredentials = ChannelCredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair, verifyPeerCallbackTag);
+ }
+
+ public override void SetCompositeCredentials(object state, ChannelCredentials channelCredentials, CallCredentials callCredentials)
+ {
+ GrpcPreconditions.CheckState(!configured);
+ configured = true;
+ using (var callCreds = callCredentials.ToNativeCredentials())
+ {
+ var nativeComposite = ChannelCredentialsSafeHandle.CreateComposite(channelCredentials.ToNativeCredentials(), callCreds);
+ if (nativeComposite.IsInvalid)
+ {
+ throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials.");
+ }
+ nativeCredentials = nativeComposite;
+ }
+ }
+
+ private class VerifyPeerCallbackRegistration
+ {
+ readonly VerifyPeerCallback verifyPeerCallback;
+ readonly NativeCallbackRegistration callbackRegistration;
+
+ public VerifyPeerCallbackRegistration(VerifyPeerCallback verifyPeerCallback)
+ {
+ this.verifyPeerCallback = verifyPeerCallback;
+ this.callbackRegistration = NativeCallbackDispatcher.RegisterCallback(HandleUniversalCallback);
+ }
+
+ public NativeCallbackRegistration CallbackRegistration => callbackRegistration;
+
+ private int HandleUniversalCallback(IntPtr arg0, IntPtr arg1, IntPtr arg2, IntPtr arg3, IntPtr arg4, IntPtr arg5)
+ {
+ return VerifyPeerCallbackHandler(arg0, arg1, arg2 != IntPtr.Zero);
+ }
+
+ private int VerifyPeerCallbackHandler(IntPtr targetName, IntPtr peerPem, bool isDestroy)
+ {
+ if (isDestroy)
+ {
+ this.callbackRegistration.Dispose();
+ return 0;
+ }
+
+ try
+ {
+ var context = new VerifyPeerContext(Marshal.PtrToStringAnsi(targetName), Marshal.PtrToStringAnsi(peerPem));
+
+ return this.verifyPeerCallback(context) ? 0 : 1;
+ }
+ catch (Exception e)
+ {
+ // eat the exception, we must not throw when inside callback from native code.
+ Logger.Error(e, "Exception occurred while invoking verify peer callback handler.");
+ // Return validation failure in case of exception.
+ return 1;
+ }
+ }
+ }
+ }
+
+ internal static class ChannelCredentialsExtensions
+ {
+ ///
+ /// Creates native object for the credentials.
+ ///
+ /// The native credentials.
+ public static ChannelCredentialsSafeHandle ToNativeCredentials(this ChannelCredentials credentials)
+ {
+ var configurator = new DefaultChannelCredentialsConfigurator();
+ credentials.InternalPopulateConfiguration(configurator, credentials);
+ return configurator.NativeCredentials;
+ }
+ }
+}