move ChannelCredentials to Grpc.Core.Api

pull/19712/head
Jan Tattermusch 5 years ago
parent 0fd05f477a
commit 47a14ba394
  1. 4
      src/csharp/Grpc.Core.Api/CallCredentials.cs
  2. 128
      src/csharp/Grpc.Core.Api/ChannelCredentials.cs
  3. 44
      src/csharp/Grpc.Core.Api/ChannelCredentialsConfiguratorBase.cs
  4. 0
      src/csharp/Grpc.Core.Api/KeyCertificatePair.cs
  5. 0
      src/csharp/Grpc.Core.Api/VerifyPeerContext.cs
  6. 14
      src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs
  7. 4
      src/csharp/Grpc.Core.Tests/FakeCredentials.cs
  8. 2
      src/csharp/Grpc.Core/Channel.cs
  9. 4
      src/csharp/Grpc.Core/ForwardedTypes.cs
  10. 130
      src/csharp/Grpc.Core/Internal/DefaultChannelCredentialsConfigurator.cs

@ -51,8 +51,8 @@ namespace Grpc.Core
}
/// <summary>
/// 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.
/// </summary>
public abstract void InternalPopulateConfiguration(CallCredentialsConfiguratorBase configurator, object state);

@ -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<ChannelCredentialsSafeHandle> cachedNativeCredentials;
// TODO: caching the instance!!!!
//readonly Lazy<ChannelCredentialsSafeHandle> cachedNativeCredentials;
/// <summary>
/// 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<ChannelCredentialsSafeHandle>(() => CreateNativeCredentials());
//this.cachedNativeCredentials = new Lazy<ChannelCredentialsSafeHandle>(() => CreateNativeCredentials());
}
/// <summary>
@ -72,36 +72,39 @@ namespace Grpc.Core
}
/// <summary>
/// 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 <c>Dispose()</c> 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.
/// </summary>
/// <returns>The native credentials.</returns>
internal ChannelCredentialsSafeHandle GetNativeCredentials()
{
return cachedNativeCredentials.Value;
}
/// <summary>
/// Creates a new native object for the credentials. May return null if insecure channel
/// should be created. For internal use only, use <see cref="GetNativeCredentials"/> instead.
/// </summary>
/// <returns>The native credentials.</returns>
internal abstract ChannelCredentialsSafeHandle CreateNativeCredentials();
public abstract void InternalPopulateConfiguration(ChannelCredentialsConfiguratorBase configurator, object state);
// / <summary>
// / 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 <c>Dispose()</c> on the returned native credentials as their lifetime
// / is managed by this class (and instances of native credentials are cached).
// / </summary>
// / <returns>The native credentials.</returns>
//internal ChannelCredentialsSafeHandle GetNativeCredentials()
//{
// return cachedNativeCredentials.Value;
//}
// / <summary>
// / Creates a new native object for the credentials. May return null if insecure channel
// / should be created. For internal use only, use <see cref="GetNativeCredentials"/> instead.
// / </summary>
// / <returns>The native credentials.</returns>
//internal abstract ChannelCredentialsSafeHandle CreateNativeCredentials();
/// <summary>
/// Returns <c>true</c> if this credential type allows being composed by <c>CompositeCredentials</c>.
/// </summary>
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
/// </summary>
public sealed class SslCredentials : ChannelCredentials
{
static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<SslCredentials>();
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()
/// <summary>
/// Populates channel credentials configurator with this instance's configuration.
/// End users never need to invoke this method as it is part of internal implementation.
/// </summary>
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;
}
/// <summary>
@ -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);
}
}
}

@ -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
{
/// <summary>
/// Base class for objects that can consume configuration from <c>CallCredentials</c> objects.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
public abstract class ChannelCredentialsConfiguratorBase
{
/// <summary>
/// Configures the credentials to use insecure credentials.
/// </summary>
public abstract void SetInsecureCredentials(object state);
/// <summary>
/// Configures the credentials to use <c>SslCredentials</c>.
/// </summary>
public abstract void SetSslCredentials(object state, string rootCertificates, KeyCertificatePair keyCertificatePair, VerifyPeerCallback verifyPeerCallback);
/// <summary>
/// Configures the credentials to use composite channel credentials (a composite of channel credentials and call credentials).
/// </summary>
public abstract void SetCompositeCredentials(object state, ChannelCredentials channelCredentials, CallCredentials callCredentials);
}
}

@ -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.");
}

@ -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
}
}

@ -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);

@ -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))]

@ -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
{
/// <summary>
/// Creates native call credential objects from instances of <c>ChannelCredentials</c>.
/// </summary>
internal class DefaultChannelCredentialsConfigurator : ChannelCredentialsConfiguratorBase
{
static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<DefaultCallCredentialsConfigurator>();
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
{
/// <summary>
/// Creates native object for the credentials.
/// </summary>
/// <returns>The native credentials.</returns>
public static ChannelCredentialsSafeHandle ToNativeCredentials(this ChannelCredentials credentials)
{
var configurator = new DefaultChannelCredentialsConfigurator();
credentials.InternalPopulateConfiguration(configurator, credentials);
return configurator.NativeCredentials;
}
}
}
Loading…
Cancel
Save