diff options
Diffstat (limited to 'src/csharp')
-rw-r--r-- | src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs | 37 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/FakeCredentials.cs | 10 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/MetadataTest.cs | 2 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Channel.cs | 2 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/ChannelCredentials.cs | 39 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Internal/AsyncCall.cs | 27 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Internal/AsyncCallBase.cs | 32 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Internal/AsyncCallServer.cs | 10 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Metadata.cs | 4 | ||||
-rwxr-xr-x | src/csharp/Grpc.Core/Version.csproj.include | 2 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/VersionInfo.cs | 4 | ||||
-rwxr-xr-x | src/csharp/build_packages_dotnetcli.bat | 2 | ||||
-rw-r--r-- | src/csharp/build_unitypackage.bat | 2 |
13 files changed, 131 insertions, 42 deletions
diff --git a/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs b/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs index 62cc904a61..843d88bfb6 100644 --- a/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs +++ b/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs @@ -17,13 +17,7 @@ #endregion using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using Grpc.Core; using Grpc.Core.Internal; -using Grpc.Core.Utils; using NUnit.Framework; namespace Grpc.Core.Tests @@ -44,9 +38,38 @@ namespace Grpc.Core.Tests Assert.Throws(typeof(ArgumentNullException), () => ChannelCredentials.Create(null, new FakeCallCredentials())); Assert.Throws(typeof(ArgumentNullException), () => ChannelCredentials.Create(new FakeChannelCredentials(true), null)); - + // forbid composing non-composable Assert.Throws(typeof(ArgumentException), () => ChannelCredentials.Create(new FakeChannelCredentials(false), new FakeCallCredentials())); } + + [Test] + public void ChannelCredentials_NativeCredentialsAreReused() + { + // 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(); + 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()); + Assert.AreSame(ex1, ex2); + } + + internal class ChannelCredentialsWithCreateNativeThrows : ChannelCredentials + { + internal override bool IsComposable => false; + + internal override ChannelCredentialsSafeHandle CreateNativeCredentials() + { + 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 7d658576e5..f23c9e9757 100644 --- a/src/csharp/Grpc.Core.Tests/FakeCredentials.cs +++ b/src/csharp/Grpc.Core.Tests/FakeCredentials.cs @@ -16,15 +16,7 @@ #endregion -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using Grpc.Core; using Grpc.Core.Internal; -using Grpc.Core.Utils; -using NUnit.Framework; namespace Grpc.Core.Tests { @@ -42,7 +34,7 @@ namespace Grpc.Core.Tests get { return composable; } } - internal override ChannelCredentialsSafeHandle ToNativeCredentials() + internal override ChannelCredentialsSafeHandle CreateNativeCredentials() { return null; } diff --git a/src/csharp/Grpc.Core.Tests/MetadataTest.cs b/src/csharp/Grpc.Core.Tests/MetadataTest.cs index 8916731757..171c5c470e 100644 --- a/src/csharp/Grpc.Core.Tests/MetadataTest.cs +++ b/src/csharp/Grpc.Core.Tests/MetadataTest.cs @@ -66,6 +66,8 @@ namespace Grpc.Core.Tests new Metadata.Entry("0123456789abc", "XYZ"); new Metadata.Entry("-abc", "XYZ"); new Metadata.Entry("a_bc_", "XYZ"); + new Metadata.Entry("abc.xyz", "XYZ"); + new Metadata.Entry("abc.xyz-bin", new byte[] {1, 2, 3}); Assert.Throws(typeof(ArgumentException), () => new Metadata.Entry("abc[", "xyz")); Assert.Throws(typeof(ArgumentException), () => new Metadata.Entry("abc/", "xyz")); } diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs index e9930b6fbc..7912b06b71 100644 --- a/src/csharp/Grpc.Core/Channel.cs +++ b/src/csharp/Grpc.Core/Channel.cs @@ -72,9 +72,9 @@ namespace Grpc.Core this.environment = GrpcEnvironment.AddRef(); this.completionQueue = this.environment.PickCompletionQueue(); - using (var nativeCredentials = credentials.ToNativeCredentials()) using (var nativeChannelArgs = ChannelOptions.CreateChannelArgs(this.options.Values)) { + var nativeCredentials = credentials.GetNativeCredentials(); if (nativeCredentials != null) { this.handle = ChannelSafeHandle.CreateSecure(nativeCredentials, target, nativeChannelArgs); diff --git a/src/csharp/Grpc.Core/ChannelCredentials.cs b/src/csharp/Grpc.Core/ChannelCredentials.cs index ba482897d7..3ce32f31b7 100644 --- a/src/csharp/Grpc.Core/ChannelCredentials.cs +++ b/src/csharp/Grpc.Core/ChannelCredentials.cs @@ -31,6 +31,19 @@ namespace Grpc.Core public abstract class ChannelCredentials { static readonly ChannelCredentials InsecureInstance = new InsecureCredentialsImpl(); + readonly Lazy<ChannelCredentialsSafeHandle> cachedNativeCredentials; + + /// <summary> + /// Creates a new instance of channel credentials + /// </summary> + public ChannelCredentials() + { + // 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. + this.cachedNativeCredentials = new Lazy<ChannelCredentialsSafeHandle>(() => CreateNativeCredentials()); + } /// <summary> /// Returns instance of credentials that provides no security and @@ -57,11 +70,22 @@ namespace Grpc.Core } /// <summary> - /// Creates native object for the credentials. May return null if insecure channel - /// should be created. + /// 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 ToNativeCredentials(); + internal abstract ChannelCredentialsSafeHandle CreateNativeCredentials(); /// <summary> /// Returns <c>true</c> if this credential type allows being composed by <c>CompositeCredentials</c>. @@ -73,7 +97,7 @@ namespace Grpc.Core private sealed class InsecureCredentialsImpl : ChannelCredentials { - internal override ChannelCredentialsSafeHandle ToNativeCredentials() + internal override ChannelCredentialsSafeHandle CreateNativeCredentials() { return null; } @@ -145,7 +169,7 @@ namespace Grpc.Core get { return true; } } - internal override ChannelCredentialsSafeHandle ToNativeCredentials() + internal override ChannelCredentialsSafeHandle CreateNativeCredentials() { return ChannelCredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair); } @@ -173,12 +197,11 @@ namespace Grpc.Core GrpcPreconditions.CheckArgument(channelCredentials.IsComposable, "Supplied channel credentials do not allow composition."); } - internal override ChannelCredentialsSafeHandle ToNativeCredentials() + internal override ChannelCredentialsSafeHandle CreateNativeCredentials() { - using (var channelCreds = channelCredentials.ToNativeCredentials()) using (var callCreds = callCredentials.ToNativeCredentials()) { - var nativeComposite = ChannelCredentialsSafeHandle.CreateComposite(channelCreds, callCreds); + 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."); diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs index 3c9e090ba4..66902f3caa 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs @@ -325,9 +325,18 @@ namespace Grpc.Core.Internal } } - protected override void OnAfterReleaseResources() + protected override void OnAfterReleaseResourcesLocked() { details.Channel.RemoveCallReference(this); + } + + protected override void OnAfterReleaseResourcesUnlocked() + { + // If cancellation callback is in progress, this can block + // so we need to do this outside of call's lock to prevent + // deadlock. + // See https://github.com/grpc/grpc/issues/14777 + // See https://github.com/dotnet/corefx/issues/14903 cancellationTokenRegistration?.Dispose(); } @@ -448,6 +457,7 @@ namespace Grpc.Core.Internal TResponse msg = default(TResponse); var deserializeException = TryDeserialize(receivedMessage, out msg); + bool releasedResources; lock (myLock) { finished = true; @@ -464,7 +474,12 @@ namespace Grpc.Core.Internal streamingWriteTcs = null; } - ReleaseResourcesIfPossible(); + releasedResources = ReleaseResourcesIfPossible(); + } + + if (releasedResources) + { + OnAfterReleaseResourcesUnlocked(); } responseHeadersTcs.SetResult(responseHeaders); @@ -494,6 +509,7 @@ namespace Grpc.Core.Internal TaskCompletionSource<object> delayedStreamingWriteTcs = null; + bool releasedResources; lock (myLock) { finished = true; @@ -504,7 +520,12 @@ namespace Grpc.Core.Internal streamingWriteTcs = null; } - ReleaseResourcesIfPossible(); + releasedResources = ReleaseResourcesIfPossible(); + } + + if (releasedResources) + { + OnAfterReleaseResourcesUnlocked(); } if (delayedStreamingWriteTcs != null) diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs index 3273c26b88..5a53049e4b 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs @@ -196,10 +196,14 @@ namespace Grpc.Core.Internal call.Dispose(); } disposed = true; - OnAfterReleaseResources(); + OnAfterReleaseResourcesLocked(); } - protected virtual void OnAfterReleaseResources() + protected virtual void OnAfterReleaseResourcesLocked() + { + } + + protected virtual void OnAfterReleaseResourcesUnlocked() { } @@ -235,6 +239,7 @@ namespace Grpc.Core.Internal { bool delayCompletion = false; TaskCompletionSource<object> origTcs = null; + bool releasedResources; lock (myLock) { if (!success && !finished && IsClient) { @@ -252,7 +257,12 @@ namespace Grpc.Core.Internal streamingWriteTcs = null; } - ReleaseResourcesIfPossible(); + releasedResources = ReleaseResourcesIfPossible(); + } + + if (releasedResources) + { + OnAfterReleaseResourcesUnlocked(); } if (!success) @@ -282,9 +292,15 @@ namespace Grpc.Core.Internal /// </summary> protected void HandleSendStatusFromServerFinished(bool success) { + bool releasedResources; lock (myLock) { - ReleaseResourcesIfPossible(); + releasedResources = ReleaseResourcesIfPossible(); + } + + if (releasedResources) + { + OnAfterReleaseResourcesUnlocked(); } if (!success) @@ -310,6 +326,7 @@ namespace Grpc.Core.Internal var deserializeException = (success && receivedMessage != null) ? TryDeserialize(receivedMessage, out msg) : null; TaskCompletionSource<TRead> origTcs = null; + bool releasedResources; lock (myLock) { origTcs = streamingReadTcs; @@ -332,7 +349,12 @@ namespace Grpc.Core.Internal streamingReadTcs = null; } - ReleaseResourcesIfPossible(); + releasedResources = ReleaseResourcesIfPossible(); + } + + if (releasedResources) + { + OnAfterReleaseResourcesUnlocked(); } if (deserializeException != null && !IsClient) diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs index 11acb27533..0ceca4abb8 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs @@ -184,7 +184,7 @@ namespace Grpc.Core.Internal throw new InvalidOperationException("Call be only called for client calls"); } - protected override void OnAfterReleaseResources() + protected override void OnAfterReleaseResourcesLocked() { server.RemoveCallReference(this); } @@ -206,6 +206,7 @@ namespace Grpc.Core.Internal { // NOTE: because this event is a result of batch containing GRPC_OP_RECV_CLOSE_ON_SERVER, // success will be always set to true. + bool releasedResources; lock (myLock) { finished = true; @@ -217,7 +218,12 @@ namespace Grpc.Core.Internal streamingReadTcs = new TaskCompletionSource<TRequest>(); streamingReadTcs.SetResult(default(TRequest)); } - ReleaseResourcesIfPossible(); + releasedResources = ReleaseResourcesIfPossible(); + } + + if (releasedResources) + { + OnAfterReleaseResourcesUnlocked(); } if (cancelled) diff --git a/src/csharp/Grpc.Core/Metadata.cs b/src/csharp/Grpc.Core/Metadata.cs index 0e4456278c..281952d6d4 100644 --- a/src/csharp/Grpc.Core/Metadata.cs +++ b/src/csharp/Grpc.Core/Metadata.cs @@ -225,7 +225,7 @@ namespace Grpc.Core /// </summary> public class Entry { - private static readonly Regex ValidKeyRegex = new Regex("^[a-z0-9_-]+$"); + private static readonly Regex ValidKeyRegex = new Regex("^[.a-z0-9_-]+$"); readonly string key; readonly string value; @@ -360,7 +360,7 @@ namespace Grpc.Core { var normalized = GrpcPreconditions.CheckNotNull(key, "key").ToLowerInvariant(); GrpcPreconditions.CheckArgument(ValidKeyRegex.IsMatch(normalized), - "Metadata entry key not valid. Keys can only contain lowercase alphanumeric characters, underscores and hyphens."); + "Metadata entry key not valid. Keys can only contain lowercase alphanumeric characters, underscores, hyphens and dots."); return normalized; } diff --git a/src/csharp/Grpc.Core/Version.csproj.include b/src/csharp/Grpc.Core/Version.csproj.include index 45bd8ebd85..18515ea1e8 100755 --- a/src/csharp/Grpc.Core/Version.csproj.include +++ b/src/csharp/Grpc.Core/Version.csproj.include @@ -1,7 +1,7 @@ <!-- This file is generated --> <Project> <PropertyGroup> - <GrpcCsharpVersion>1.15.0-dev</GrpcCsharpVersion> + <GrpcCsharpVersion>1.16.0-dev</GrpcCsharpVersion> <GoogleProtobufVersion>3.6.1</GoogleProtobufVersion> </PropertyGroup> </Project> diff --git a/src/csharp/Grpc.Core/VersionInfo.cs b/src/csharp/Grpc.Core/VersionInfo.cs index 295e0f2577..55d09dda7a 100644 --- a/src/csharp/Grpc.Core/VersionInfo.cs +++ b/src/csharp/Grpc.Core/VersionInfo.cs @@ -33,11 +33,11 @@ namespace Grpc.Core /// <summary> /// Current <c>AssemblyFileVersion</c> of gRPC C# assemblies /// </summary> - public const string CurrentAssemblyFileVersion = "1.15.0.0"; + public const string CurrentAssemblyFileVersion = "1.16.0.0"; /// <summary> /// Current version of gRPC C# /// </summary> - public const string CurrentVersion = "1.15.0-dev"; + public const string CurrentVersion = "1.16.0-dev"; } } diff --git a/src/csharp/build_packages_dotnetcli.bat b/src/csharp/build_packages_dotnetcli.bat index 8f38f0aa20..24d016104c 100755 --- a/src/csharp/build_packages_dotnetcli.bat +++ b/src/csharp/build_packages_dotnetcli.bat @@ -13,7 +13,7 @@ @rem limitations under the License. @rem Current package versions -set VERSION=1.15.0-dev +set VERSION=1.16.0-dev @rem Adjust the location of nuget.exe set NUGET=C:\nuget\nuget.exe diff --git a/src/csharp/build_unitypackage.bat b/src/csharp/build_unitypackage.bat index 9c53114b84..4e7ac4e414 100644 --- a/src/csharp/build_unitypackage.bat +++ b/src/csharp/build_unitypackage.bat @@ -13,7 +13,7 @@ @rem limitations under the License. @rem Current package versions -set VERSION=1.15.0-dev +set VERSION=1.16.0-dev @rem Adjust the location of nuget.exe set NUGET=C:\nuget\nuget.exe |