diff options
author | 2015-08-12 19:53:03 +0200 | |
---|---|---|
committer | 2015-08-12 19:53:03 +0200 | |
commit | 9b6248fc6d3be0e3970e19138bedd0655e4565e8 (patch) | |
tree | ee2852ba33f5638d9d3973c944ccc604b5e6fb99 /src/csharp | |
parent | b457cd831ad519d5ec023005ba6ccfb52ff4c9cb (diff) | |
parent | 3b226512d4aa84d58e1e61ce3f9adb9315f2d619 (diff) |
Merge branch 'master' of github.com:grpc/grpc into the-ultimate-showdown
Conflicts:
src/node/ext/call.cc
src/node/ext/channel.cc
Diffstat (limited to 'src/csharp')
54 files changed, 751 insertions, 528 deletions
diff --git a/src/csharp/Grpc.Auth/GoogleCredential.cs b/src/csharp/Grpc.Auth/GoogleCredential.cs deleted file mode 100644 index 9936cf583c..0000000000 --- a/src/csharp/Grpc.Auth/GoogleCredential.cs +++ /dev/null @@ -1,125 +0,0 @@ -#region Copyright notice and license - -// Copyright 2015, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#endregion - -using System; -using System.Collections.Generic; -using System.IO; -using System.Security.Cryptography; -using System.Threading; -using System.Threading.Tasks; - -using Google.Apis.Auth.OAuth2; -using Google.Apis.Auth.OAuth2.Responses; -using Newtonsoft.Json.Linq; -using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.Security; - -namespace Grpc.Auth -{ - // TODO(jtattermusch): Remove this class once possible. - /// <summary> - /// A temporary placeholder for Google credential from - /// Google Auth library for .NET. It emulates the usage pattern - /// for Usable auth. - /// </summary> - public class GoogleCredential - { - private const string GoogleApplicationCredentialsEnvName = "GOOGLE_APPLICATION_CREDENTIALS"; - private const string ClientEmailFieldName = "client_email"; - private const string PrivateKeyFieldName = "private_key"; - - private ServiceCredential credential; - - private GoogleCredential(ServiceCredential credential) - { - this.credential = credential; - } - - public static GoogleCredential GetApplicationDefault() - { - return new GoogleCredential(null); - } - - public bool IsCreateScopedRequired - { - get - { - return true; - } - } - - public GoogleCredential CreateScoped(IEnumerable<string> scopes) - { - var credsPath = Environment.GetEnvironmentVariable(GoogleApplicationCredentialsEnvName); - if (credsPath == null) - { - // Default to ComputeCredentials if path to JSON key is not set. - // ComputeCredential is not scoped actually, but for our use case it's - // fine to treat is as such. - return new GoogleCredential(new ComputeCredential(new ComputeCredential.Initializer())); - } - - JObject jsonCredentialParameters = JObject.Parse(File.ReadAllText(credsPath)); - string clientEmail = jsonCredentialParameters.GetValue(ClientEmailFieldName).Value<string>(); - string privateKeyString = jsonCredentialParameters.GetValue(PrivateKeyFieldName).Value<string>(); - - var serviceCredential = new ServiceAccountCredential( - new ServiceAccountCredential.Initializer(clientEmail) - { - Scopes = scopes, - }.FromPrivateKey(privateKeyString)); - return new GoogleCredential(serviceCredential); - } - - public Task<bool> RequestAccessTokenAsync(CancellationToken taskCancellationToken) - { - return credential.RequestAccessTokenAsync(taskCancellationToken); - } - - public TokenResponse Token - { - get - { - return credential.Token; - } - } - - internal ServiceCredential InternalCredential - { - get - { - return credential; - } - } - } -} diff --git a/src/csharp/Grpc.Auth/Grpc.Auth.csproj b/src/csharp/Grpc.Auth/Grpc.Auth.csproj index 8e5036832d..930a34b0c3 100644 --- a/src/csharp/Grpc.Auth/Grpc.Auth.csproj +++ b/src/csharp/Grpc.Auth/Grpc.Auth.csproj @@ -11,7 +11,7 @@ <AssemblyName>Grpc.Auth</AssemblyName> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <DocumentationFile>bin\$(Configuration)\Grpc.Auth.Xml</DocumentationFile> - <NuGetPackageImportStamp>9b408026</NuGetPackageImportStamp> + <NuGetPackageImportStamp>4f8487a9</NuGetPackageImportStamp> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> @@ -41,28 +41,32 @@ <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> </PropertyGroup> <ItemGroup> - <Reference Include="BouncyCastle.Crypto"> + <Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath> </Reference> - <Reference Include="Google.Apis.Auth, Version=1.9.2.27817, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> + <Reference Include="Google.Apis.Auth, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\Google.Apis.Auth.1.9.2\lib\net40\Google.Apis.Auth.dll</HintPath> + <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath> </Reference> - <Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.2.27820, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> + <Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.3.19383, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\Google.Apis.Auth.1.9.2\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath> + <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath> </Reference> - <Reference Include="Google.Apis.Core, Version=1.9.2.27816, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> + <Reference Include="Google.Apis.Core, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\Google.Apis.Core.1.9.2\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath> + <HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath> </Reference> - <Reference Include="Microsoft.Threading.Tasks"> + <Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath> </Reference> - <Reference Include="Microsoft.Threading.Tasks.Extensions"> + <Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath> </Reference> - <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop"> + <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath> </Reference> <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> @@ -87,7 +91,6 @@ <Link>Version.cs</Link> </Compile> <Compile Include="Properties\AssemblyInfo.cs" /> - <Compile Include="GoogleCredential.cs" /> <Compile Include="OAuth2Interceptors.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> diff --git a/src/csharp/Grpc.Auth/OAuth2Interceptors.cs b/src/csharp/Grpc.Auth/OAuth2Interceptors.cs index cc9d2c175f..d628a83246 100644 --- a/src/csharp/Grpc.Auth/OAuth2Interceptors.cs +++ b/src/csharp/Grpc.Auth/OAuth2Interceptors.cs @@ -54,7 +54,7 @@ namespace Grpc.Auth /// </summary> public static MetadataInterceptorDelegate FromCredential(GoogleCredential googleCredential) { - var interceptor = new OAuth2Interceptor(googleCredential.InternalCredential, SystemClock.Default); + var interceptor = new OAuth2Interceptor(googleCredential, SystemClock.Default); return new MetadataInterceptorDelegate(interceptor.InterceptHeaders); } @@ -66,7 +66,7 @@ namespace Grpc.Auth public static MetadataInterceptorDelegate FromAccessToken(string oauth2Token) { Preconditions.CheckNotNull(oauth2Token); - return new MetadataInterceptorDelegate((metadata) => + return new MetadataInterceptorDelegate((authUri, metadata) => { metadata.Add(OAuth2Interceptor.CreateBearerTokenHeader(oauth2Token)); }); @@ -80,10 +80,10 @@ namespace Grpc.Auth private const string AuthorizationHeader = "Authorization"; private const string Schema = "Bearer"; - private ServiceCredential credential; + private ITokenAccess credential; private IClock clock; - public OAuth2Interceptor(ServiceCredential credential, IClock clock) + public OAuth2Interceptor(ITokenAccess credential, IClock clock) { this.credential = credential; this.clock = clock; @@ -94,23 +94,15 @@ namespace Grpc.Auth /// </summary> /// <param name="cancellationToken"></param> /// <returns></returns> - public string GetAccessToken(CancellationToken cancellationToken) + public string GetAccessToken(string authUri, CancellationToken cancellationToken) { - if (credential.Token == null || credential.Token.IsExpired(clock)) - { - // TODO(jtattermusch): Parallel requests will spawn multiple requests to refresh the token once the token expires. - // TODO(jtattermusch): Rethink synchronous wait to obtain the result. - if (!credential.RequestAccessTokenAsync(cancellationToken).Result) - { - throw new InvalidOperationException("The access token has expired but we can't refresh it"); - } - } - return credential.Token.AccessToken; + // TODO(jtattermusch): Rethink synchronous wait to obtain the result. + return credential.GetAccessTokenForRequestAsync(authUri, cancellationToken: cancellationToken).GetAwaiter().GetResult(); } - public void InterceptHeaders(Metadata metadata) + public void InterceptHeaders(string authUri, Metadata metadata) { - var accessToken = GetAccessToken(CancellationToken.None); + var accessToken = GetAccessToken(authUri, CancellationToken.None); metadata.Add(CreateBearerTokenHeader(accessToken)); } diff --git a/src/csharp/Grpc.Auth/app.config b/src/csharp/Grpc.Auth/app.config index 0a82bb4f16..84d7534d65 100644 --- a/src/csharp/Grpc.Auth/app.config +++ b/src/csharp/Grpc.Auth/app.config @@ -10,6 +10,10 @@ <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.0.0.0" /> </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="Google.Apis.Core" publicKeyToken="4b01fa6e34db77ab" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-1.9.2.38523" newVersion="1.9.2.38523" /> + </dependentAssembly> </assemblyBinding> </runtime> </configuration>
\ No newline at end of file diff --git a/src/csharp/Grpc.Auth/packages.config b/src/csharp/Grpc.Auth/packages.config index 29be953bf3..7a02c95db9 100644 --- a/src/csharp/Grpc.Auth/packages.config +++ b/src/csharp/Grpc.Auth/packages.config @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="BouncyCastle" version="1.7.0" targetFramework="net45" /> - <package id="Google.Apis.Auth" version="1.9.2" targetFramework="net45" /> - <package id="Google.Apis.Core" version="1.9.2" targetFramework="net45" /> + <package id="Google.Apis.Auth" version="1.9.3" targetFramework="net45" /> + <package id="Google.Apis.Core" version="1.9.3" targetFramework="net45" /> <package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" /> <package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" /> <package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" /> diff --git a/src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs b/src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs index df09857efe..52be77c846 100644 --- a/src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs +++ b/src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs @@ -67,9 +67,9 @@ namespace Grpc.Core.Internal.Tests [Test] public void ConstructorPreconditions() { - Assert.Throws(typeof(NullReferenceException), () => { new ChannelOption(null, "abc"); }); - Assert.Throws(typeof(NullReferenceException), () => { new ChannelOption(null, 1); }); - Assert.Throws(typeof(NullReferenceException), () => { new ChannelOption("abc", null); }); + Assert.Throws(typeof(ArgumentNullException), () => { new ChannelOption(null, "abc"); }); + Assert.Throws(typeof(ArgumentNullException), () => { new ChannelOption(null, 1); }); + Assert.Throws(typeof(ArgumentNullException), () => { new ChannelOption("abc", null); }); } [Test] diff --git a/src/csharp/Grpc.Core.Tests/ChannelTest.cs b/src/csharp/Grpc.Core.Tests/ChannelTest.cs index 60b45176e5..2787572924 100644 --- a/src/csharp/Grpc.Core.Tests/ChannelTest.cs +++ b/src/csharp/Grpc.Core.Tests/ChannelTest.cs @@ -50,7 +50,7 @@ namespace Grpc.Core.Tests [Test] public void Constructor_RejectsInvalidParams() { - Assert.Throws(typeof(NullReferenceException), () => new Channel(null, Credentials.Insecure)); + Assert.Throws(typeof(ArgumentNullException), () => new Channel(null, Credentials.Insecure)); } [Test] @@ -72,11 +72,11 @@ namespace Grpc.Core.Tests } [Test] - public void Target() + public void ResolvedTarget() { using (var channel = new Channel("127.0.0.1", Credentials.Insecure)) { - Assert.IsTrue(channel.Target.Contains("127.0.0.1")); + Assert.IsTrue(channel.ResolvedTarget.Contains("127.0.0.1")); } } diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index c5fc85b3fe..e49fdb5268 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -138,7 +138,7 @@ namespace Grpc.Core.Tests helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) => { string result = ""; - await requestStream.ForEach(async (request) => + await requestStream.ForEachAsync(async (request) => { result += request; }); @@ -147,7 +147,7 @@ namespace Grpc.Core.Tests }); var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall()); - await call.RequestStream.WriteAll(new string[] { "A", "B", "C" }); + await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" }); Assert.AreEqual("ABC", await call.ResponseAsync); } @@ -159,7 +159,7 @@ namespace Grpc.Core.Tests helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) => { barrier.SetResult(null); - await requestStream.ToList(); + await requestStream.ToListAsync(); return ""; }); diff --git a/src/csharp/Grpc.Core.Tests/CompressionTest.cs b/src/csharp/Grpc.Core.Tests/CompressionTest.cs index ac0c3d6b5f..9547683f60 100644 --- a/src/csharp/Grpc.Core.Tests/CompressionTest.cs +++ b/src/csharp/Grpc.Core.Tests/CompressionTest.cs @@ -90,7 +90,7 @@ namespace Grpc.Core.Tests { helper.DuplexStreamingHandler = new DuplexStreamingServerMethod<string, string>(async (requestStream, responseStream, context) => { - await requestStream.ToList(); + await requestStream.ToListAsync(); context.WriteOptions = new WriteOptions(WriteFlags.NoCompress); @@ -122,7 +122,7 @@ namespace Grpc.Core.Tests await call.RequestStream.CompleteAsync(); - await call.ResponseStream.ToList(); + await call.ResponseStream.ToListAsync(); } } } diff --git a/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs b/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs index a7f5075874..db5f953b0e 100644 --- a/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs +++ b/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs @@ -110,6 +110,14 @@ namespace Grpc.Core.Tests helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) => { + Assert.Throws(typeof(ArgumentException), () => + { + // Trying to override deadline while propagating deadline from parent call will throw. + Calls.BlockingUnaryCall(helper.CreateUnaryCall( + new CallOptions(deadline: DateTime.UtcNow.AddDays(8), + propagationToken: context.CreatePropagationToken())), ""); + }); + var callOptions = new CallOptions(propagationToken: context.CreatePropagationToken()); return await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz"); }); @@ -118,5 +126,28 @@ namespace Grpc.Core.Tests await call.RequestStream.CompleteAsync(); Assert.AreEqual("PASS", await call); } + + [Test] + public async Task SuppressDeadlinePropagation() + { + helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => + { + Assert.AreEqual(DateTime.MaxValue, context.Deadline); + return "PASS"; + }); + + helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) => + { + Assert.IsTrue(context.CancellationToken.CanBeCanceled); + + var callOptions = new CallOptions(propagationToken: context.CreatePropagationToken(new ContextPropagationOptions(propagateDeadline: false))); + return await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz"); + }); + + var cts = new CancellationTokenSource(); + var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(deadline: DateTime.UtcNow.AddDays(7)))); + await call.RequestStream.CompleteAsync(); + Assert.AreEqual("PASS", await call); + } } } diff --git a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs index b642286b11..bb69648d8b 100644 --- a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs +++ b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs @@ -153,27 +153,23 @@ namespace Grpc.Core.Tests return channel; } - public CallInvocationDetails<string, string> CreateUnaryCall(CallOptions options = null) + public CallInvocationDetails<string, string> CreateUnaryCall(CallOptions options = default(CallOptions)) { - options = options ?? new CallOptions(); return new CallInvocationDetails<string, string>(channel, UnaryMethod, options); } - public CallInvocationDetails<string, string> CreateClientStreamingCall(CallOptions options = null) + public CallInvocationDetails<string, string> CreateClientStreamingCall(CallOptions options = default(CallOptions)) { - options = options ?? new CallOptions(); return new CallInvocationDetails<string, string>(channel, ClientStreamingMethod, options); } - public CallInvocationDetails<string, string> CreateServerStreamingCall(CallOptions options = null) + public CallInvocationDetails<string, string> CreateServerStreamingCall(CallOptions options = default(CallOptions)) { - options = options ?? new CallOptions(); return new CallInvocationDetails<string, string>(channel, ServerStreamingMethod, options); } - public CallInvocationDetails<string, string> CreateDuplexStreamingCall(CallOptions options = null) + public CallInvocationDetails<string, string> CreateDuplexStreamingCall(CallOptions options = default(CallOptions)) { - options = options ?? new CallOptions(); return new CallInvocationDetails<string, string>(channel, DuplexStreamingMethod, options); } diff --git a/src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs b/src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs index 8925041ba4..981b8ea3c8 100644 --- a/src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs +++ b/src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs @@ -84,7 +84,7 @@ namespace Grpc.Core.Tests { helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => { - Assert.Throws(typeof(NullReferenceException), async () => await context.WriteResponseHeadersAsync(null)); + Assert.Throws(typeof(ArgumentNullException), async () => await context.WriteResponseHeadersAsync(null)); return "PASS"; }); @@ -129,7 +129,7 @@ namespace Grpc.Core.Tests }); var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), ""); - var responses = await call.ResponseStream.ToList(); + var responses = await call.ResponseStream.ToListAsync(); CollectionAssert.AreEqual(new[] { "A", "B" }, responses); } } diff --git a/src/csharp/Grpc.Core/CallInvocationDetails.cs b/src/csharp/Grpc.Core/CallInvocationDetails.cs index eb23a3a209..6565073fc5 100644 --- a/src/csharp/Grpc.Core/CallInvocationDetails.cs +++ b/src/csharp/Grpc.Core/CallInvocationDetails.cs @@ -40,30 +40,60 @@ namespace Grpc.Core /// <summary> /// Details about a client-side call to be invoked. /// </summary> - public class CallInvocationDetails<TRequest, TResponse> + public struct CallInvocationDetails<TRequest, TResponse> { readonly Channel channel; readonly string method; readonly string host; readonly Marshaller<TRequest> requestMarshaller; readonly Marshaller<TResponse> responseMarshaller; - readonly CallOptions options; + CallOptions options; + /// <summary> + /// Initializes a new instance of the <see cref="Grpc.Core.CallInvocationDetails`2"/> struct. + /// </summary> + /// <param name="channel">Channel to use for this call.</param> + /// <param name="method">Method to call.</param> + /// <param name="options">Call options.</param> public CallInvocationDetails(Channel channel, Method<TRequest, TResponse> method, CallOptions options) : - this(channel, method.FullName, null, method.RequestMarshaller, method.ResponseMarshaller, options) + this(channel, method, null, options) { } + /// <summary> + /// Initializes a new instance of the <see cref="Grpc.Core.CallInvocationDetails`2"/> struct. + /// </summary> + /// <param name="channel">Channel to use for this call.</param> + /// <param name="method">Method to call.</param> + /// <param name="host">Host that contains the method. if <c>null</c>, default host will be used.</param> + /// <param name="options">Call options.</param> + public CallInvocationDetails(Channel channel, Method<TRequest, TResponse> method, string host, CallOptions options) : + this(channel, method.FullName, host, method.RequestMarshaller, method.ResponseMarshaller, options) + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="Grpc.Core.CallInvocationDetails`2"/> struct. + /// </summary> + /// <param name="channel">Channel to use for this call.</param> + /// <param name="method">Qualified method name.</param> + /// <param name="host">Host that contains the method.</param> + /// <param name="requestMarshaller">Request marshaller.</param> + /// <param name="responseMarshaller">Response marshaller.</param> + /// <param name="options">Call options.</param> public CallInvocationDetails(Channel channel, string method, string host, Marshaller<TRequest> requestMarshaller, Marshaller<TResponse> responseMarshaller, CallOptions options) { - this.channel = Preconditions.CheckNotNull(channel); - this.method = Preconditions.CheckNotNull(method); + this.channel = Preconditions.CheckNotNull(channel, "channel"); + this.method = Preconditions.CheckNotNull(method, "method"); this.host = host; - this.requestMarshaller = Preconditions.CheckNotNull(requestMarshaller); - this.responseMarshaller = Preconditions.CheckNotNull(responseMarshaller); - this.options = Preconditions.CheckNotNull(options); + this.requestMarshaller = Preconditions.CheckNotNull(requestMarshaller, "requestMarshaller"); + this.responseMarshaller = Preconditions.CheckNotNull(responseMarshaller, "responseMarshaller"); + this.options = options; } + /// <summary> + /// Get channel associated with this call. + /// </summary> public Channel Channel { get @@ -72,6 +102,9 @@ namespace Grpc.Core } } + /// <summary> + /// Gets name of method to be called. + /// </summary> public string Method { get @@ -80,6 +113,9 @@ namespace Grpc.Core } } + /// <summary> + /// Get name of host. + /// </summary> public string Host { get @@ -88,6 +124,9 @@ namespace Grpc.Core } } + /// <summary> + /// Gets marshaller used to serialize requests. + /// </summary> public Marshaller<TRequest> RequestMarshaller { get @@ -96,6 +135,9 @@ namespace Grpc.Core } } + /// <summary> + /// Gets marshaller used to deserialized responses. + /// </summary> public Marshaller<TResponse> ResponseMarshaller { get @@ -104,6 +146,9 @@ namespace Grpc.Core } } + /// <summary> + /// Gets the call options. + /// </summary> public CallOptions Options { get @@ -111,5 +156,16 @@ namespace Grpc.Core return options; } } + + /// <summary> + /// Returns new instance of <see cref="CallInvocationDetails"/> with + /// <c>Options</c> set to the value provided. Values of all other fields are preserved. + /// </summary> + public CallInvocationDetails<TRequest, TResponse> WithOptions(CallOptions options) + { + var newDetails = this; + newDetails.options = options; + return newDetails; + } } } diff --git a/src/csharp/Grpc.Core/CallOptions.cs b/src/csharp/Grpc.Core/CallOptions.cs index 0d82b5a28e..3dfe80b48c 100644 --- a/src/csharp/Grpc.Core/CallOptions.cs +++ b/src/csharp/Grpc.Core/CallOptions.cs @@ -42,29 +42,28 @@ namespace Grpc.Core /// <summary> /// Options for calls made by client. /// </summary> - public class CallOptions + public struct CallOptions { - readonly Metadata headers; - readonly DateTime deadline; - readonly CancellationToken cancellationToken; - readonly WriteOptions writeOptions; - readonly ContextPropagationToken propagationToken; + Metadata headers; + DateTime? deadline; + CancellationToken cancellationToken; + WriteOptions writeOptions; + ContextPropagationToken propagationToken; /// <summary> - /// Creates a new instance of <c>CallOptions</c>. + /// Creates a new instance of <c>CallOptions</c> struct. /// </summary> /// <param name="headers">Headers to be sent with the call.</param> /// <param name="deadline">Deadline for the call to finish. null means no deadline.</param> /// <param name="cancellationToken">Can be used to request cancellation of the call.</param> /// <param name="writeOptions">Write options that will be used for this call.</param> /// <param name="propagationToken">Context propagation token obtained from <see cref="ServerCallContext"/>.</param> - public CallOptions(Metadata headers = null, DateTime? deadline = null, CancellationToken? cancellationToken = null, + public CallOptions(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken), WriteOptions writeOptions = null, ContextPropagationToken propagationToken = null) { - // TODO(jtattermusch): consider only creating metadata object once it's really needed. - this.headers = headers ?? new Metadata(); - this.deadline = deadline ?? (propagationToken != null ? propagationToken.Deadline : DateTime.MaxValue); - this.cancellationToken = cancellationToken ?? (propagationToken != null ? propagationToken.CancellationToken : CancellationToken.None); + this.headers = headers; + this.deadline = deadline; + this.cancellationToken = cancellationToken; this.writeOptions = writeOptions; this.propagationToken = propagationToken; } @@ -80,7 +79,7 @@ namespace Grpc.Core /// <summary> /// Call deadline. /// </summary> - public DateTime Deadline + public DateTime? Deadline { get { return deadline; } } @@ -114,5 +113,66 @@ namespace Grpc.Core return this.propagationToken; } } + + /// <summary> + /// Returns new instance of <see cref="CallOptions"/> with + /// <c>Headers</c> set to the value provided. Values of all other fields are preserved. + /// </summary> + public CallOptions WithHeaders(Metadata headers) + { + var newOptions = this; + newOptions.headers = headers; + return newOptions; + } + + /// <summary> + /// Returns new instance of <see cref="CallOptions"/> with + /// <c>Deadline</c> set to the value provided. Values of all other fields are preserved. + /// </summary> + public CallOptions WithDeadline(DateTime deadline) + { + var newOptions = this; + newOptions.deadline = deadline; + return newOptions; + } + + /// <summary> + /// Returns new instance of <see cref="CallOptions"/> with + /// <c>CancellationToken</c> set to the value provided. Values of all other fields are preserved. + /// </summary> + public CallOptions WithCancellationToken(CancellationToken cancellationToken) + { + var newOptions = this; + newOptions.cancellationToken = cancellationToken; + return newOptions; + } + + /// <summary> + /// Returns a new instance of <see cref="CallOptions"/> with + /// all previously unset values set to their defaults and deadline and cancellation + /// token propagated when appropriate. + /// </summary> + internal CallOptions Normalize() + { + var newOptions = this; + if (propagationToken != null) + { + if (propagationToken.Options.IsPropagateDeadline) + { + Preconditions.CheckArgument(!newOptions.deadline.HasValue, + "Cannot propagate deadline from parent call. The deadline has already been set explicitly."); + newOptions.deadline = propagationToken.ParentDeadline; + } + if (propagationToken.Options.IsPropagateCancellation) + { + Preconditions.CheckArgument(!newOptions.cancellationToken.CanBeCanceled, + "Cannot propagate cancellation token from parent call. The cancellation token has already been set to a non-default value."); + } + } + + newOptions.headers = newOptions.headers ?? Metadata.Empty; + newOptions.deadline = newOptions.deadline ?? DateTime.MaxValue; + return newOptions; + } } } diff --git a/src/csharp/Grpc.Core/Calls.cs b/src/csharp/Grpc.Core/Calls.cs index 00a8cabf82..7067456638 100644 --- a/src/csharp/Grpc.Core/Calls.cs +++ b/src/csharp/Grpc.Core/Calls.cs @@ -31,8 +31,6 @@ #endregion -using System; -using System.Threading; using System.Threading.Tasks; using Grpc.Core.Internal; @@ -40,9 +38,20 @@ namespace Grpc.Core { /// <summary> /// Helper methods for generated clients to make RPC calls. + /// Most users will use this class only indirectly and will be + /// making calls using client object generated from protocol + /// buffer definition files. /// </summary> public static class Calls { + /// <summary> + /// Invokes a simple remote call in a blocking fashion. + /// </summary> + /// <returns>The response.</returns> + /// <param name="call">The call defintion.</param> + /// <param name="req">Request message.</param> + /// <typeparam name="TRequest">Type of request message.</typeparam> + /// <typeparam name="TResponse">The of response message.</typeparam> public static TResponse BlockingUnaryCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call, TRequest req) where TRequest : class where TResponse : class @@ -51,6 +60,14 @@ namespace Grpc.Core return asyncCall.UnaryCall(req); } + /// <summary> + /// Invokes a simple remote call asynchronously. + /// </summary> + /// <returns>An awaitable call object providing access to the response.</returns> + /// <param name="call">The call defintion.</param> + /// <param name="req">Request message.</param> + /// <typeparam name="TRequest">Type of request message.</typeparam> + /// <typeparam name="TResponse">The of response message.</typeparam> public static AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call, TRequest req) where TRequest : class where TResponse : class @@ -60,6 +77,15 @@ namespace Grpc.Core return new AsyncUnaryCall<TResponse>(asyncResult, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); } + /// <summary> + /// Invokes a server streaming call asynchronously. + /// In server streaming scenario, client sends on request and server responds with a stream of responses. + /// </summary> + /// <returns>A call object providing access to the asynchronous response stream.</returns> + /// <param name="call">The call defintion.</param> + /// <param name="req">Request message.</param> + /// <typeparam name="TRequest">Type of request message.</typeparam> + /// <typeparam name="TResponse">The of response messages.</typeparam> public static AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call, TRequest req) where TRequest : class where TResponse : class @@ -70,6 +96,13 @@ namespace Grpc.Core return new AsyncServerStreamingCall<TResponse>(responseStream, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); } + /// <summary> + /// Invokes a client streaming call asynchronously. + /// In client streaming scenario, client sends a stream of requests and server responds with a single response. + /// </summary> + /// <returns>An awaitable call object providing access to the response.</returns> + /// <typeparam name="TRequest">Type of request messages.</typeparam> + /// <typeparam name="TResponse">The of response message.</typeparam> public static AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call) where TRequest : class where TResponse : class @@ -80,6 +113,15 @@ namespace Grpc.Core return new AsyncClientStreamingCall<TRequest, TResponse>(requestStream, resultTask, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); } + /// <summary> + /// Invokes a duplex streaming call asynchronously. + /// In duplex streaming scenario, client sends a stream of requests and server responds with a stream of responses. + /// The response stream is completely independent and both side can be sending messages at the same time. + /// </summary> + /// <returns>A call object providing access to the asynchronous request and response streams.</returns> + /// <param name="call">The call definition.</param> + /// <typeparam name="TRequest">Type of request messages.</typeparam> + /// <typeparam name="TResponse">Type of reponse messages.</typeparam> public static AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call) where TRequest : class where TResponse : class diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs index 9273ea4582..64c6adf2bf 100644 --- a/src/csharp/Grpc.Core/Channel.cs +++ b/src/csharp/Grpc.Core/Channel.cs @@ -49,6 +49,7 @@ namespace Grpc.Core { static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<Channel>(); + readonly string target; readonly GrpcEnvironment environment; readonly ChannelSafeHandle handle; readonly List<ChannelOption> options; @@ -58,12 +59,12 @@ namespace Grpc.Core /// Creates a channel that connects to a specific host. /// Port will default to 80 for an unsecure channel and to 443 for a secure channel. /// </summary> - /// <param name="host">The name or IP address of the host.</param> + /// <param name="target">Target of the channel.</param> /// <param name="credentials">Credentials to secure the channel.</param> /// <param name="options">Channel options.</param> - public Channel(string host, Credentials credentials, IEnumerable<ChannelOption> options = null) + public Channel(string target, Credentials credentials, IEnumerable<ChannelOption> options = null) { - Preconditions.CheckNotNull(host); + this.target = Preconditions.CheckNotNull(target, "target"); this.environment = GrpcEnvironment.GetInstance(); this.options = options != null ? new List<ChannelOption>(options) : new List<ChannelOption>(); @@ -73,11 +74,11 @@ namespace Grpc.Core { if (nativeCredentials != null) { - this.handle = ChannelSafeHandle.CreateSecure(nativeCredentials, host, nativeChannelArgs); + this.handle = ChannelSafeHandle.CreateSecure(nativeCredentials, target, nativeChannelArgs); } else { - this.handle = ChannelSafeHandle.CreateInsecure(host, nativeChannelArgs); + this.handle = ChannelSafeHandle.CreateInsecure(target, nativeChannelArgs); } } } @@ -131,8 +132,8 @@ namespace Grpc.Core return tcs.Task; } - /// <summary> Address of the remote endpoint in URI format.</summary> - public string Target + /// <summary>Resolved address of the remote endpoint in URI format.</summary> + public string ResolvedTarget { get { @@ -140,6 +141,15 @@ namespace Grpc.Core } } + /// <summary>The original target used to create the channel.</summary> + public string Target + { + get + { + return this.target; + } + } + /// <summary> /// Allows explicitly requesting channel to connect without starting an RPC. /// Returned task completes once state Ready was seen. If the deadline is reached, diff --git a/src/csharp/Grpc.Core/ChannelOptions.cs b/src/csharp/Grpc.Core/ChannelOptions.cs index 1e0f90287a..0cb2953f2c 100644 --- a/src/csharp/Grpc.Core/ChannelOptions.cs +++ b/src/csharp/Grpc.Core/ChannelOptions.cs @@ -63,8 +63,8 @@ namespace Grpc.Core public ChannelOption(string name, string stringValue) { this.type = OptionType.String; - this.name = Preconditions.CheckNotNull(name); - this.stringValue = Preconditions.CheckNotNull(stringValue); + this.name = Preconditions.CheckNotNull(name, "name"); + this.stringValue = Preconditions.CheckNotNull(stringValue, "stringValue"); } /// <summary> @@ -75,7 +75,7 @@ namespace Grpc.Core public ChannelOption(string name, int intValue) { this.type = OptionType.Integer; - this.name = Preconditions.CheckNotNull(name); + this.name = Preconditions.CheckNotNull(name, "name"); this.intValue = intValue; } diff --git a/src/csharp/Grpc.Core/ClientBase.cs b/src/csharp/Grpc.Core/ClientBase.cs index 88494bb4ac..f46184406c 100644 --- a/src/csharp/Grpc.Core/ClientBase.cs +++ b/src/csharp/Grpc.Core/ClientBase.cs @@ -35,21 +35,26 @@ using System; using System.Collections.Generic; using Grpc.Core.Internal; +using System.Text.RegularExpressions; namespace Grpc.Core { - public delegate void MetadataInterceptorDelegate(Metadata metadata); + public delegate void MetadataInterceptorDelegate(string authUri, Metadata metadata); /// <summary> /// Base class for client-side stubs. /// </summary> public abstract class ClientBase { + static readonly Regex TrailingPortPattern = new Regex(":[0-9]+/?$"); readonly Channel channel; + readonly string authUriBase; public ClientBase(Channel channel) { this.channel = channel; + // TODO(jtattermush): we shouldn't need to hand-curate the channel.Target contents. + this.authUriBase = "https://" + TrailingPortPattern.Replace(channel.Target, "") + "/"; } /// <summary> @@ -63,6 +68,18 @@ namespace Grpc.Core } /// <summary> + /// gRPC supports multiple "hosts" being served by a single server. + /// This property can be used to set the target host explicitly. + /// By default, this will be set to <c>null</c> with the meaning + /// "use default host". + /// </summary> + public string Host + { + get; + set; + } + + /// <summary> /// Channel associated with this client. /// </summary> public Channel Channel @@ -83,10 +100,14 @@ namespace Grpc.Core var interceptor = HeaderInterceptor; if (interceptor != null) { - interceptor(options.Headers); - options.Headers.Freeze(); + if (options.Headers == null) + { + options = options.WithHeaders(new Metadata()); + } + var authUri = authUriBase + method.ServiceName; + interceptor(authUri, options.Headers); } - return new CallInvocationDetails<TRequest, TResponse>(channel, method, options); + return new CallInvocationDetails<TRequest, TResponse>(channel, method, Host, options); } } } diff --git a/src/csharp/Grpc.Core/ContextPropagationToken.cs b/src/csharp/Grpc.Core/ContextPropagationToken.cs index b6ea5115a4..2e4bfc9e47 100644 --- a/src/csharp/Grpc.Core/ContextPropagationToken.cs +++ b/src/csharp/Grpc.Core/ContextPropagationToken.cs @@ -52,7 +52,7 @@ namespace Grpc.Core /// <summary> /// Default propagation mask used by C core. /// </summary> - const ContextPropagationFlags DefaultCoreMask = (ContextPropagationFlags)0xffff; + private const ContextPropagationFlags DefaultCoreMask = (ContextPropagationFlags)0xffff; /// <summary> /// Default propagation mask used by C# - we want to propagate deadline @@ -74,6 +74,9 @@ namespace Grpc.Core this.options = options ?? ContextPropagationOptions.Default; } + /// <summary> + /// Gets the native handle of the parent call. + /// </summary> internal CallSafeHandle ParentCall { get @@ -82,7 +85,10 @@ namespace Grpc.Core } } - internal DateTime Deadline + /// <summary> + /// Gets the parent call's deadline. + /// </summary> + internal DateTime ParentDeadline { get { @@ -90,7 +96,10 @@ namespace Grpc.Core } } - internal CancellationToken CancellationToken + /// <summary> + /// Gets the parent call's cancellation token. + /// </summary> + internal CancellationToken ParentCancellationToken { get { @@ -98,6 +107,9 @@ namespace Grpc.Core } } + /// <summary> + /// Get the context propagation options. + /// </summary> internal ContextPropagationOptions Options { get @@ -105,16 +117,6 @@ namespace Grpc.Core return this.options; } } - - internal bool IsPropagateDeadline - { - get { return false; } - } - - internal bool IsPropagateCancellation - { - get { return false; } - } } /// <summary> @@ -122,7 +124,37 @@ namespace Grpc.Core /// </summary> public class ContextPropagationOptions { + /// <summary> + /// The context propagation options that will be used by default. + /// </summary> public static readonly ContextPropagationOptions Default = new ContextPropagationOptions(); + + bool propagateDeadline; + bool propagateCancellation; + + + /// <summary> + /// Creates new context propagation options. + /// </summary> + /// <param name="propagateDeadline">If set to <c>true</c> parent call's deadline will be propagated to the child call.</param> + /// <param name="propagateCancellation">If set to <c>true</c> parent call's cancellation token will be propagated to the child call.</param> + public ContextPropagationOptions(bool propagateDeadline = true, bool propagateCancellation = true) + { + this.propagateDeadline = propagateDeadline; + this.propagateCancellation = propagateCancellation; + } + + /// <value><c>true</c> if parent call's deadline should be propagated to the child call.</value> + public bool IsPropagateDeadline + { + get { return this.propagateDeadline; } + } + + /// <value><c>true</c> if parent call's cancellation token should be propagated to the child call.</value> + public bool IsPropagateCancellation + { + get { return this.propagateCancellation; } + } } /// <summary> diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index e535c47f55..9a8195e9d0 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -77,7 +77,6 @@ <Compile Include="ServerServiceDefinition.cs" /> <Compile Include="Utils\AsyncStreamExtensions.cs" /> <Compile Include="Utils\BenchmarkUtil.cs" /> - <Compile Include="Utils\ExceptionHelper.cs" /> <Compile Include="Internal\CredentialsSafeHandle.cs" /> <Compile Include="Credentials.cs" /> <Compile Include="Internal\ChannelArgsSafeHandle.cs" /> diff --git a/src/csharp/Grpc.Core/GrpcEnvironment.cs b/src/csharp/Grpc.Core/GrpcEnvironment.cs index 1bb83c9962..30d8c80235 100644 --- a/src/csharp/Grpc.Core/GrpcEnvironment.cs +++ b/src/csharp/Grpc.Core/GrpcEnvironment.cs @@ -115,7 +115,7 @@ namespace Grpc.Core /// </summary> public static void SetLogger(ILogger customLogger) { - Preconditions.CheckNotNull(customLogger); + Preconditions.CheckNotNull(customLogger, "customLogger"); logger = customLogger; } @@ -192,23 +192,5 @@ namespace Grpc.Core Logger.Info("gRPC shutdown."); } - - /// <summary> - /// Shuts down this environment asynchronously. - /// </summary> - private Task CloseAsync() - { - return Task.Run(() => - { - try - { - Close(); - } - catch (Exception e) - { - Logger.Error(e, "Error occured while shutting down GrpcEnvironment."); - } - }); - } } } diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs index 0db9d2a515..2c3e3d75ea 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs @@ -63,7 +63,7 @@ namespace Grpc.Core.Internal public AsyncCall(CallInvocationDetails<TRequest, TResponse> callDetails) : base(callDetails.RequestMarshaller.Serializer, callDetails.ResponseMarshaller.Deserializer) { - this.details = callDetails; + this.details = callDetails.WithOptions(callDetails.Options.Normalize()); this.initialMetadataSent = true; // we always send metadata at the very beginning of the call. } @@ -109,15 +109,9 @@ namespace Grpc.Core.Internal } } - try - { - // Once the blocking call returns, the result should be available synchronously. - return unaryResponseTcs.Task.Result; - } - catch (AggregateException ae) - { - throw ExceptionHelper.UnwrapRpcException(ae); - } + // Once the blocking call returns, the result should be available synchronously. + // Note that GetAwaiter().GetResult() doesn't wrap exceptions in AggregateException. + return unaryResponseTcs.Task.GetAwaiter().GetResult(); } } @@ -324,12 +318,11 @@ namespace Grpc.Core.Internal private void Initialize(CompletionQueueSafeHandle cq) { - var propagationToken = details.Options.PropagationToken; - var parentCall = propagationToken != null ? propagationToken.ParentCall : CallSafeHandle.NullInstance; + var parentCall = details.Options.PropagationToken != null ? details.Options.PropagationToken.ParentCall : CallSafeHandle.NullInstance; var call = details.Channel.Handle.CreateCall(details.Channel.Environment.CompletionRegistry, parentCall, ContextPropagationToken.DefaultMask, cq, - details.Method, details.Host, Timespec.FromDateTime(details.Options.Deadline)); + details.Method, details.Host, Timespec.FromDateTime(details.Options.Deadline.Value)); details.Channel.Environment.DebugStats.ActiveClientCalls.Increment(); InitializeInternal(call); RegisterCancellationCallback(); diff --git a/src/csharp/Grpc.Core/Internal/Timespec.cs b/src/csharp/Grpc.Core/Internal/Timespec.cs index e83d21f4a4..daf85d5f61 100644 --- a/src/csharp/Grpc.Core/Internal/Timespec.cs +++ b/src/csharp/Grpc.Core/Internal/Timespec.cs @@ -211,7 +211,7 @@ namespace Grpc.Core.Internal return Timespec.InfPast; } - Preconditions.CheckArgument(dateTime.Kind == DateTimeKind.Utc, "dateTime"); + Preconditions.CheckArgument(dateTime.Kind == DateTimeKind.Utc, "dateTime needs of kind DateTimeKind.Utc or be equal to DateTime.MaxValue or DateTime.MinValue."); try { diff --git a/src/csharp/Grpc.Core/KeyCertificatePair.cs b/src/csharp/Grpc.Core/KeyCertificatePair.cs index 5def15a656..6f691975e9 100644 --- a/src/csharp/Grpc.Core/KeyCertificatePair.cs +++ b/src/csharp/Grpc.Core/KeyCertificatePair.cs @@ -54,8 +54,8 @@ namespace Grpc.Core /// <param name="privateKey">PEM encoded private key.</param> public KeyCertificatePair(string certificateChain, string privateKey) { - this.certificateChain = Preconditions.CheckNotNull(certificateChain); - this.privateKey = Preconditions.CheckNotNull(privateKey); + this.certificateChain = Preconditions.CheckNotNull(certificateChain, "certificateChain"); + this.privateKey = Preconditions.CheckNotNull(privateKey, "privateKey"); } /// <summary> diff --git a/src/csharp/Grpc.Core/Logging/ConsoleLogger.cs b/src/csharp/Grpc.Core/Logging/ConsoleLogger.cs index c67765c78d..382481d871 100644 --- a/src/csharp/Grpc.Core/Logging/ConsoleLogger.cs +++ b/src/csharp/Grpc.Core/Logging/ConsoleLogger.cs @@ -42,16 +42,21 @@ namespace Grpc.Core.Logging readonly Type forType; readonly string forTypeString; + /// <summary>Creates a console logger not associated to any specific type.</summary> public ConsoleLogger() : this(null) { } + /// <summary>Creates a console logger that logs messsage specific for given type.</summary> private ConsoleLogger(Type forType) { this.forType = forType; this.forTypeString = forType != null ? forType.FullName + " " : ""; } - + + /// <summary> + /// Returns a logger associated with the specified type. + /// </summary> public ILogger ForType<T>() { if (typeof(T) == forType) @@ -61,31 +66,37 @@ namespace Grpc.Core.Logging return new ConsoleLogger(typeof(T)); } + /// <summary>Logs a message with severity Debug.</summary> public void Debug(string message, params object[] formatArgs) { Log("D", message, formatArgs); } + /// <summary>Logs a message with severity Info.</summary> public void Info(string message, params object[] formatArgs) { Log("I", message, formatArgs); } + /// <summary>Logs a message with severity Warning.</summary> public void Warning(string message, params object[] formatArgs) { Log("W", message, formatArgs); } + /// <summary>Logs a message and an associated exception with severity Warning.</summary> public void Warning(Exception exception, string message, params object[] formatArgs) { Log("W", message + " " + exception, formatArgs); } + /// <summary>Logs a message with severity Error.</summary> public void Error(string message, params object[] formatArgs) { Log("E", message, formatArgs); } + /// <summary>Logs a message and an associated exception with severity Error.</summary> public void Error(Exception exception, string message, params object[] formatArgs) { Log("E", message + " " + exception, formatArgs); diff --git a/src/csharp/Grpc.Core/Logging/ILogger.cs b/src/csharp/Grpc.Core/Logging/ILogger.cs index 0d58f133e3..61e0c91388 100644 --- a/src/csharp/Grpc.Core/Logging/ILogger.cs +++ b/src/csharp/Grpc.Core/Logging/ILogger.cs @@ -42,16 +42,22 @@ namespace Grpc.Core.Logging /// <summary>Returns a logger associated with the specified type.</summary> ILogger ForType<T>(); + /// <summary>Logs a message with severity Debug.</summary> void Debug(string message, params object[] formatArgs); + /// <summary>Logs a message with severity Info.</summary> void Info(string message, params object[] formatArgs); + /// <summary>Logs a message with severity Warning.</summary> void Warning(string message, params object[] formatArgs); + /// <summary>Logs a message and an associated exception with severity Warning.</summary> void Warning(Exception exception, string message, params object[] formatArgs); + /// <summary>Logs a message with severity Error.</summary> void Error(string message, params object[] formatArgs); + /// <summary>Logs a message and an associated exception with severity Error.</summary> void Error(Exception exception, string message, params object[] formatArgs); } } diff --git a/src/csharp/Grpc.Core/Marshaller.cs b/src/csharp/Grpc.Core/Marshaller.cs index 8b1a929074..f38cb0863f 100644 --- a/src/csharp/Grpc.Core/Marshaller.cs +++ b/src/csharp/Grpc.Core/Marshaller.cs @@ -37,19 +37,27 @@ using Grpc.Core.Utils; namespace Grpc.Core { /// <summary> - /// For serializing and deserializing messages. + /// Encapsulates the logic for serializing and deserializing messages. /// </summary> public struct Marshaller<T> { readonly Func<T, byte[]> serializer; readonly Func<byte[], T> deserializer; + /// <summary> + /// Initializes a new marshaller. + /// </summary> + /// <param name="serializer">Function that will be used to serialize messages.</param> + /// <param name="deserializer">Function that will be used to deserialize messages.</param> public Marshaller(Func<T, byte[]> serializer, Func<byte[], T> deserializer) { - this.serializer = Preconditions.CheckNotNull(serializer); - this.deserializer = Preconditions.CheckNotNull(deserializer); + this.serializer = Preconditions.CheckNotNull(serializer, "serializer"); + this.deserializer = Preconditions.CheckNotNull(deserializer, "deserializer"); } + /// <summary> + /// Gets the serializer function. + /// </summary> public Func<T, byte[]> Serializer { get @@ -58,6 +66,9 @@ namespace Grpc.Core } } + /// <summary> + /// Gets the deserializer function. + /// </summary> public Func<byte[], T> Deserializer { get @@ -72,11 +83,17 @@ namespace Grpc.Core /// </summary> public static class Marshallers { + /// <summary> + /// Creates a marshaller from specified serializer and deserializer. + /// </summary> public static Marshaller<T> Create<T>(Func<T, byte[]> serializer, Func<byte[], T> deserializer) { return new Marshaller<T>(serializer, deserializer); } + /// <summary> + /// Returns a marshaller for <c>string</c> type. This is useful for testing. + /// </summary> public static Marshaller<string> StringMarshaller { get diff --git a/src/csharp/Grpc.Core/Metadata.cs b/src/csharp/Grpc.Core/Metadata.cs index a58dbdbc93..9db2abf46e 100644 --- a/src/csharp/Grpc.Core/Metadata.cs +++ b/src/csharp/Grpc.Core/Metadata.cs @@ -186,15 +186,15 @@ namespace Grpc.Core public Entry(string key, byte[] valueBytes) { - this.key = Preconditions.CheckNotNull(key); + this.key = Preconditions.CheckNotNull(key, "key"); this.value = null; - this.valueBytes = Preconditions.CheckNotNull(valueBytes); + this.valueBytes = Preconditions.CheckNotNull(valueBytes, "valueBytes"); } public Entry(string key, string value) { - this.key = Preconditions.CheckNotNull(key); - this.value = Preconditions.CheckNotNull(value); + this.key = Preconditions.CheckNotNull(key, "key"); + this.value = Preconditions.CheckNotNull(value, "value"); this.valueBytes = null; } diff --git a/src/csharp/Grpc.Core/Method.cs b/src/csharp/Grpc.Core/Method.cs index cc047ac9f8..4c208b4a26 100644 --- a/src/csharp/Grpc.Core/Method.cs +++ b/src/csharp/Grpc.Core/Method.cs @@ -41,14 +41,21 @@ namespace Grpc.Core /// </summary> public enum MethodType { - Unary, // Unary request, unary response. - ClientStreaming, // Streaming request, unary response. - ServerStreaming, // Unary request, streaming response. - DuplexStreaming // Streaming request, streaming response. + /// <summary>Single request sent from client, single response received from server.</summary> + Unary, + + /// <summary>Stream of request sent from client, single response received from server.</summary> + ClientStreaming, + + /// <summary>Single request sent from client, stream of responses received from server.</summary> + ServerStreaming, + + /// <summary>Both server and client can stream arbitrary number of requests and responses simultaneously.</summary> + DuplexStreaming } /// <summary> - /// A description of a service method. + /// A description of a remote method. /// </summary> public class Method<TRequest, TResponse> { @@ -59,16 +66,27 @@ namespace Grpc.Core readonly Marshaller<TResponse> responseMarshaller; readonly string fullName; + /// <summary> + /// Initializes a new instance of the <c>Method</c> class. + /// </summary> + /// <param name="type">Type of method.</param> + /// <param name="serviceName">Name of service this method belongs to.</param> + /// <param name="name">Unqualified name of the method.</param> + /// <param name="requestMarshaller">Marshaller used for request messages.</param> + /// <param name="responseMarshaller">Marshaller used for response messages.</param> public Method(MethodType type, string serviceName, string name, Marshaller<TRequest> requestMarshaller, Marshaller<TResponse> responseMarshaller) { this.type = type; - this.serviceName = Preconditions.CheckNotNull(serviceName); - this.name = Preconditions.CheckNotNull(name); - this.requestMarshaller = Preconditions.CheckNotNull(requestMarshaller); - this.responseMarshaller = Preconditions.CheckNotNull(responseMarshaller); - this.fullName = GetFullName(serviceName); + this.serviceName = Preconditions.CheckNotNull(serviceName, "serviceName"); + this.name = Preconditions.CheckNotNull(name, "name"); + this.requestMarshaller = Preconditions.CheckNotNull(requestMarshaller, "requestMarshaller"); + this.responseMarshaller = Preconditions.CheckNotNull(responseMarshaller, "responseMarshaller"); + this.fullName = GetFullName(serviceName, name); } + /// <summary> + /// Gets the type of the method. + /// </summary> public MethodType Type { get @@ -77,6 +95,9 @@ namespace Grpc.Core } } + /// <summary> + /// Gets the name of the service to which this method belongs. + /// </summary> public string ServiceName { get @@ -85,6 +106,9 @@ namespace Grpc.Core } } + /// <summary> + /// Gets the unqualified name of the method. + /// </summary> public string Name { get @@ -93,6 +117,9 @@ namespace Grpc.Core } } + /// <summary> + /// Gets the marshaller used for request messages. + /// </summary> public Marshaller<TRequest> RequestMarshaller { get @@ -101,6 +128,9 @@ namespace Grpc.Core } } + /// <summary> + /// Gets the marshaller used for response messages. + /// </summary> public Marshaller<TResponse> ResponseMarshaller { get @@ -108,7 +138,11 @@ namespace Grpc.Core return this.responseMarshaller; } } - + + /// <summary> + /// Gets the fully qualified name of the method. On the server side, methods are dispatched + /// based on this name. + /// </summary> public string FullName { get @@ -120,9 +154,9 @@ namespace Grpc.Core /// <summary> /// Gets full name of the method including the service name. /// </summary> - internal string GetFullName(string serviceName) + internal static string GetFullName(string serviceName, string methodName) { - return "/" + Preconditions.CheckNotNull(serviceName) + "/" + this.Name; + return "/" + serviceName + "/" + methodName; } } } diff --git a/src/csharp/Grpc.Core/RpcException.cs b/src/csharp/Grpc.Core/RpcException.cs index c58578286b..cac417e626 100644 --- a/src/csharp/Grpc.Core/RpcException.cs +++ b/src/csharp/Grpc.Core/RpcException.cs @@ -36,22 +36,34 @@ using System; namespace Grpc.Core { /// <summary> - /// Thrown when remote procedure call fails. + /// Thrown when remote procedure call fails. Every <c>RpcException</c> is associated with a resulting <see cref="Status"/> of the call. /// </summary> public class RpcException : Exception { private readonly Status status; + /// <summary> + /// Creates a new <c>RpcException</c> associated with given status. + /// </summary> + /// <param name="status">Resulting status of a call.</param> public RpcException(Status status) : base(status.ToString()) { this.status = status; } + /// <summary> + /// Creates a new <c>RpcException</c> associated with given status and message. + /// </summary> + /// <param name="status">Resulting status of a call.</param> + /// <param name="message">The exception message.</param> public RpcException(Status status, string message) : base(message) { this.status = status; } + /// <summary> + /// Resulting status of the call. + /// </summary> public Status Status { get diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs index eb5b043d1c..c76f126026 100644 --- a/src/csharp/Grpc.Core/Server.cs +++ b/src/csharp/Grpc.Core/Server.cs @@ -192,7 +192,7 @@ namespace Grpc.Core { lock (myLock) { - Preconditions.CheckNotNull(serverPort.Credentials); + Preconditions.CheckNotNull(serverPort.Credentials, "serverPort"); Preconditions.CheckState(!startRequested); var address = string.Format("{0}:{1}", serverPort.Host, serverPort.Port); int boundPort; diff --git a/src/csharp/Grpc.Core/ServerCredentials.cs b/src/csharp/Grpc.Core/ServerCredentials.cs index c11a1ede08..3c6703d30e 100644 --- a/src/csharp/Grpc.Core/ServerCredentials.cs +++ b/src/csharp/Grpc.Core/ServerCredentials.cs @@ -91,7 +91,7 @@ namespace Grpc.Core { this.keyCertificatePairs = new List<KeyCertificatePair>(keyCertificatePairs).AsReadOnly(); Preconditions.CheckArgument(this.keyCertificatePairs.Count > 0, - "At least one KeyCertificatePair needs to be provided"); + "At least one KeyCertificatePair needs to be provided."); if (forceClientAuth) { Preconditions.CheckNotNull(rootCertificates, diff --git a/src/csharp/Grpc.Core/ServerMethods.cs b/src/csharp/Grpc.Core/ServerMethods.cs index d457770203..1f119a80ff 100644 --- a/src/csharp/Grpc.Core/ServerMethods.cs +++ b/src/csharp/Grpc.Core/ServerMethods.cs @@ -31,12 +31,8 @@ #endregion -using System; -using System.Threading; using System.Threading.Tasks; -using Grpc.Core.Internal; - namespace Grpc.Core { /// <summary> diff --git a/src/csharp/Grpc.Core/ServerPort.cs b/src/csharp/Grpc.Core/ServerPort.cs index 55e4bd0062..598404d045 100644 --- a/src/csharp/Grpc.Core/ServerPort.cs +++ b/src/csharp/Grpc.Core/ServerPort.cs @@ -62,9 +62,9 @@ namespace Grpc.Core /// <param name="credentials">credentials to use to secure this port.</param> public ServerPort(string host, int port, ServerCredentials credentials) { - this.host = Preconditions.CheckNotNull(host); + this.host = Preconditions.CheckNotNull(host, "host"); this.port = port; - this.credentials = Preconditions.CheckNotNull(credentials); + this.credentials = Preconditions.CheckNotNull(credentials, "credentials"); } /// <summary> diff --git a/src/csharp/Grpc.Core/ServerServiceDefinition.cs b/src/csharp/Grpc.Core/ServerServiceDefinition.cs index a00d156e52..94b0a320c3 100644 --- a/src/csharp/Grpc.Core/ServerServiceDefinition.cs +++ b/src/csharp/Grpc.Core/ServerServiceDefinition.cs @@ -79,7 +79,7 @@ namespace Grpc.Core where TRequest : class where TResponse : class { - callHandlers.Add(method.GetFullName(serviceName), ServerCalls.UnaryCall(method, handler)); + callHandlers.Add(method.FullName, ServerCalls.UnaryCall(method, handler)); return this; } @@ -89,7 +89,7 @@ namespace Grpc.Core where TRequest : class where TResponse : class { - callHandlers.Add(method.GetFullName(serviceName), ServerCalls.ClientStreamingCall(method, handler)); + callHandlers.Add(method.FullName, ServerCalls.ClientStreamingCall(method, handler)); return this; } @@ -99,7 +99,7 @@ namespace Grpc.Core where TRequest : class where TResponse : class { - callHandlers.Add(method.GetFullName(serviceName), ServerCalls.ServerStreamingCall(method, handler)); + callHandlers.Add(method.FullName, ServerCalls.ServerStreamingCall(method, handler)); return this; } @@ -109,7 +109,7 @@ namespace Grpc.Core where TRequest : class where TResponse : class { - callHandlers.Add(method.GetFullName(serviceName), ServerCalls.DuplexStreamingCall(method, handler)); + callHandlers.Add(method.FullName, ServerCalls.DuplexStreamingCall(method, handler)); return this; } diff --git a/src/csharp/Grpc.Core/Status.cs b/src/csharp/Grpc.Core/Status.cs index 754f6cb3ca..6bd8dc820b 100644 --- a/src/csharp/Grpc.Core/Status.cs +++ b/src/csharp/Grpc.Core/Status.cs @@ -29,13 +29,12 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion -using System; -using System.Runtime.InteropServices; +using Grpc.Core.Utils; namespace Grpc.Core { /// <summary> - /// Represents RPC result. + /// Represents RPC result, which consists of <see cref="StatusCode"/> and an optional detail string. /// </summary> public struct Status { @@ -52,6 +51,11 @@ namespace Grpc.Core readonly StatusCode statusCode; readonly string detail; + /// <summary> + /// Creates a new instance of <c>Status</c>. + /// </summary> + /// <param name="statusCode">Status code.</param> + /// <param name="detail">Detail.</param> public Status(StatusCode statusCode, string detail) { this.statusCode = statusCode; @@ -80,6 +84,9 @@ namespace Grpc.Core } } + /// <summary> + /// Returns a <see cref="System.String"/> that represents the current <see cref="Grpc.Core.Status"/>. + /// </summary> public override string ToString() { return string.Format("Status(StatusCode={0}, Detail=\"{1}\")", statusCode, detail); diff --git a/src/csharp/Grpc.Core/StatusCode.cs b/src/csharp/Grpc.Core/StatusCode.cs index a9696fa469..90606955af 100644 --- a/src/csharp/Grpc.Core/StatusCode.cs +++ b/src/csharp/Grpc.Core/StatusCode.cs @@ -31,8 +31,6 @@ #endregion -using System; - namespace Grpc.Core { /// <summary> @@ -41,101 +39,101 @@ namespace Grpc.Core /// </summary> public enum StatusCode { - /* Not an error; returned on success */ + /// <summary>Not an error; returned on success.</summary> OK = 0, - /* The operation was cancelled (typically by the caller). */ + + /// <summary>The operation was cancelled (typically by the caller).</summary> Cancelled = 1, - /* Unknown error. An example of where this error may be returned is - if a Status value received from another address space belongs to - an error-space that is not known in this address space. Also - errors raised by APIs that do not return enough error information - may be converted to this error. */ + + /// <summary> + /// Unknown error. An example of where this error may be returned is + /// if a Status value received from another address space belongs to + /// an error-space that is not known in this address space. Also + /// errors raised by APIs that do not return enough error information + /// may be converted to this error. + /// </summary> Unknown = 2, - /* Client specified an invalid argument. Note that this differs - from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments - that are problematic regardless of the state of the system - (e.g., a malformed file name). */ + + /// <summary> + /// Client specified an invalid argument. Note that this differs + /// from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments + /// that are problematic regardless of the state of the system + /// (e.g., a malformed file name). + /// </summary> InvalidArgument = 3, - /* Deadline expired before operation could complete. For operations - that change the state of the system, this error may be returned - even if the operation has completed successfully. For example, a - successful response from a server could have been delayed long - enough for the deadline to expire. */ + + /// <summary> + /// Deadline expired before operation could complete. For operations + /// that change the state of the system, this error may be returned + /// even if the operation has completed successfully. For example, a + /// successful response from a server could have been delayed long + /// enough for the deadline to expire. + /// </summary> DeadlineExceeded = 4, - /* Some requested entity (e.g., file or directory) was not found. */ + + /// <summary>Some requested entity (e.g., file or directory) was not found.</summary> NotFound = 5, - /* Some entity that we attempted to create (e.g., file or directory) - already exists. */ + + /// <summary>Some entity that we attempted to create (e.g., file or directory) already exists.</summary> AlreadyExists = 6, - /* The caller does not have permission to execute the specified - operation. PERMISSION_DENIED must not be used for rejections - caused by exhausting some resource (use RESOURCE_EXHAUSTED - instead for those errors). PERMISSION_DENIED must not be - used if the caller can not be identified (use UNAUTHENTICATED - instead for those errors). */ + + /// <summary> + /// The caller does not have permission to execute the specified + /// operation. PERMISSION_DENIED must not be used for rejections + /// caused by exhausting some resource (use RESOURCE_EXHAUSTED + /// instead for those errors). PERMISSION_DENIED must not be + /// used if the caller can not be identified (use UNAUTHENTICATED + /// instead for those errors). + /// </summary> PermissionDenied = 7, - /* The request does not have valid authentication credentials for the - operation. */ + + /// <summary>The request does not have valid authentication credentials for the operation.</summary> Unauthenticated = 16, - /* Some resource has been exhausted, perhaps a per-user quota, or - perhaps the entire file system is out of space. */ + + /// <summary> + /// Some resource has been exhausted, perhaps a per-user quota, or + /// perhaps the entire file system is out of space. + /// </summary> ResourceExhausted = 8, - /* Operation was rejected because the system is not in a state - required for the operation's execution. For example, directory - to be deleted may be non-empty, an rmdir operation is applied to - a non-directory, etc. - - A litmus test that may help a service implementor in deciding - between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE: - (a) Use UNAVAILABLE if the client can retry just the failing call. - (b) Use ABORTED if the client should retry at a higher-level - (e.g., restarting a read-modify-write sequence). - (c) Use FAILED_PRECONDITION if the client should not retry until - the system state has been explicitly fixed. E.g., if an "rmdir" - fails because the directory is non-empty, FAILED_PRECONDITION - should be returned since the client should not retry unless - they have first fixed up the directory by deleting files from it. - (d) Use FAILED_PRECONDITION if the client performs conditional - REST Get/Update/Delete on a resource and the resource on the - server does not match the condition. E.g., conflicting - read-modify-write on the same resource. */ + + /// <summary> + /// Operation was rejected because the system is not in a state + /// required for the operation's execution. For example, directory + /// to be deleted may be non-empty, an rmdir operation is applied to + /// a non-directory, etc. + /// </summary> FailedPrecondition = 9, - /* The operation was aborted, typically due to a concurrency issue - like sequencer check failures, transaction aborts, etc. - See litmus test above for deciding between FAILED_PRECONDITION, - ABORTED, and UNAVAILABLE. */ + /// <summary> + /// The operation was aborted, typically due to a concurrency issue + /// like sequencer check failures, transaction aborts, etc. + /// </summary> Aborted = 10, - /* Operation was attempted past the valid range. E.g., seeking or - reading past end of file. - - Unlike INVALID_ARGUMENT, this error indicates a problem that may - be fixed if the system state changes. For example, a 32-bit file - system will generate INVALID_ARGUMENT if asked to read at an - offset that is not in the range [0,2^32-1], but it will generate - OUT_OF_RANGE if asked to read from an offset past the current - file size. - - There is a fair bit of overlap between FAILED_PRECONDITION and - OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific - error) when it applies so that callers who are iterating through - a space can easily look for an OUT_OF_RANGE error to detect when - they are done. */ + + /// <summary> + /// Operation was attempted past the valid range. E.g., seeking or + /// reading past end of file. + /// </summary> OutOfRange = 11, - /* Operation is not implemented or not supported/enabled in this service. */ + + /// <summary>Operation is not implemented or not supported/enabled in this service.</summary> Unimplemented = 12, - /* Internal errors. Means some invariants expected by underlying - system has been broken. If you see one of these errors, - something is very broken. */ + + /// <summary> + /// Internal errors. Means some invariants expected by underlying + /// system has been broken. If you see one of these errors, + /// something is very broken. + /// </summary> Internal = 13, - /* The service is currently unavailable. This is a most likely a - transient condition and may be corrected by retrying with - a backoff. - See litmus test above for deciding between FAILED_PRECONDITION, - ABORTED, and UNAVAILABLE. */ + /// <summary> + /// The service is currently unavailable. This is a most likely a + /// transient condition and may be corrected by retrying with + /// a backoff. + /// </summary> Unavailable = 14, - /* Unrecoverable data loss or corruption. */ + + /// <summary>Unrecoverable data loss or corruption.</summary> DataLoss = 15 } } diff --git a/src/csharp/Grpc.Core/Utils/AsyncStreamExtensions.cs b/src/csharp/Grpc.Core/Utils/AsyncStreamExtensions.cs index 8a748b45a8..cdf1e51026 100644 --- a/src/csharp/Grpc.Core/Utils/AsyncStreamExtensions.cs +++ b/src/csharp/Grpc.Core/Utils/AsyncStreamExtensions.cs @@ -33,7 +33,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; namespace Grpc.Core.Utils @@ -46,7 +45,7 @@ namespace Grpc.Core.Utils /// <summary> /// Reads the entire stream and executes an async action for each element. /// </summary> - public static async Task ForEach<T>(this IAsyncStreamReader<T> streamReader, Func<T, Task> asyncAction) + public static async Task ForEachAsync<T>(this IAsyncStreamReader<T> streamReader, Func<T, Task> asyncAction) where T : class { while (await streamReader.MoveNext()) @@ -58,7 +57,7 @@ namespace Grpc.Core.Utils /// <summary> /// Reads the entire stream and creates a list containing all the elements read. /// </summary> - public static async Task<List<T>> ToList<T>(this IAsyncStreamReader<T> streamReader) + public static async Task<List<T>> ToListAsync<T>(this IAsyncStreamReader<T> streamReader) where T : class { var result = new List<T>(); @@ -73,7 +72,7 @@ namespace Grpc.Core.Utils /// Writes all elements from given enumerable to the stream. /// Completes the stream afterwards unless close = false. /// </summary> - public static async Task WriteAll<T>(this IClientStreamWriter<T> streamWriter, IEnumerable<T> elements, bool complete = true) + public static async Task WriteAllAsync<T>(this IClientStreamWriter<T> streamWriter, IEnumerable<T> elements, bool complete = true) where T : class { foreach (var element in elements) @@ -89,7 +88,7 @@ namespace Grpc.Core.Utils /// <summary> /// Writes all elements from given enumerable to the stream. /// </summary> - public static async Task WriteAll<T>(this IServerStreamWriter<T> streamWriter, IEnumerable<T> elements) + public static async Task WriteAllAsync<T>(this IServerStreamWriter<T> streamWriter, IEnumerable<T> elements) where T : class { foreach (var element in elements) diff --git a/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs b/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs index 82653c3a1f..eb3a5b16e3 100644 --- a/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs +++ b/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs @@ -39,6 +39,9 @@ using System.Threading.Tasks; namespace Grpc.Core.Utils { + /// <summary> + /// Utility methods to run microbenchmarks. + /// </summary> public static class BenchmarkUtil { /// <summary> diff --git a/src/csharp/Grpc.Core/Utils/ExceptionHelper.cs b/src/csharp/Grpc.Core/Utils/ExceptionHelper.cs deleted file mode 100644 index c4d6bee058..0000000000 --- a/src/csharp/Grpc.Core/Utils/ExceptionHelper.cs +++ /dev/null @@ -1,57 +0,0 @@ -#region Copyright notice and license - -// Copyright 2015, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#endregion - -using System; - -namespace Grpc.Core.Utils -{ - public static class ExceptionHelper - { - /// <summary> - /// If inner exceptions contain RpcException, rethrows it. - /// Otherwise, rethrows the original aggregate exception. - /// Always throws, the exception return type is here only to make the. - /// </summary> - public static Exception UnwrapRpcException(AggregateException ae) - { - foreach (var e in ae.InnerExceptions) - { - if (e is RpcException) - { - throw e; - } - } - throw ae; - } - } -} diff --git a/src/csharp/Grpc.Core/Utils/Preconditions.cs b/src/csharp/Grpc.Core/Utils/Preconditions.cs index aeb5d210a7..374262f87a 100644 --- a/src/csharp/Grpc.Core/Utils/Preconditions.cs +++ b/src/csharp/Grpc.Core/Utils/Preconditions.cs @@ -32,17 +32,16 @@ #endregion using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading.Tasks; namespace Grpc.Core.Utils { + /// <summary> + /// Utility methods to simplify checking preconditions in the code. + /// </summary> public static class Preconditions { /// <summary> - /// Throws ArgumentException if condition is false. + /// Throws <see cref="ArgumentException"/> if condition is false. /// </summary> public static void CheckArgument(bool condition) { @@ -53,7 +52,7 @@ namespace Grpc.Core.Utils } /// <summary> - /// Throws ArgumentException with given message if condition is false. + /// Throws <see cref="ArgumentException"/> with given message if condition is false. /// </summary> public static void CheckArgument(bool condition, string errorMessage) { @@ -64,31 +63,31 @@ namespace Grpc.Core.Utils } /// <summary> - /// Throws NullReferenceException if reference is null. + /// Throws <see cref="ArgumentNullException"/> if reference is null. /// </summary> public static T CheckNotNull<T>(T reference) { if (reference == null) { - throw new NullReferenceException(); + throw new ArgumentNullException(); } return reference; } /// <summary> - /// Throws NullReferenceException with given message if reference is null. + /// Throws <see cref="ArgumentNullException"/> if reference is null. /// </summary> - public static T CheckNotNull<T>(T reference, string errorMessage) + public static T CheckNotNull<T>(T reference, string paramName) { if (reference == null) { - throw new NullReferenceException(errorMessage); + throw new ArgumentNullException(paramName); } return reference; } /// <summary> - /// Throws InvalidOperationException if condition is false. + /// Throws <see cref="InvalidOperationException"/> if condition is false. /// </summary> public static void CheckState(bool condition) { @@ -99,7 +98,7 @@ namespace Grpc.Core.Utils } /// <summary> - /// Throws InvalidOperationException with given message if condition is false. + /// Throws <see cref="InvalidOperationException"/> with given message if condition is false. /// </summary> public static void CheckState(bool condition, string errorMessage) { diff --git a/src/csharp/Grpc.Core/Version.cs b/src/csharp/Grpc.Core/Version.cs index d2a029fbb4..d02b301cac 100644 --- a/src/csharp/Grpc.Core/Version.cs +++ b/src/csharp/Grpc.Core/Version.cs @@ -1,5 +1,37 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + using System.Reflection; -using System.Runtime.CompilerServices; // The current version of gRPC C#. [assembly: AssemblyVersion(Grpc.Core.VersionInfo.CurrentVersion + ".0")] diff --git a/src/csharp/Grpc.Core/VersionInfo.cs b/src/csharp/Grpc.Core/VersionInfo.cs index 939372e237..b6dbd3b49c 100644 --- a/src/csharp/Grpc.Core/VersionInfo.cs +++ b/src/csharp/Grpc.Core/VersionInfo.cs @@ -1,8 +1,41 @@ -using System.Reflection; -using System.Runtime.CompilerServices; +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion namespace Grpc.Core { + /// <summary> + /// Provides info about current version of gRPC. + /// </summary> public static class VersionInfo { /// <summary> diff --git a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs index 73d2a1ca9b..fdef950f09 100644 --- a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs +++ b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs @@ -109,7 +109,7 @@ namespace math.Tests { using (var call = client.Fib(new FibArgs.Builder { Limit = 6 }.Build())) { - var responses = await call.ResponseStream.ToList(); + var responses = await call.ResponseStream.ToListAsync(); CollectionAssert.AreEqual(new List<long> { 1, 1, 2, 3, 5, 8 }, responses.ConvertAll((n) => n.Num_)); } @@ -151,7 +151,7 @@ namespace math.Tests using (var call = client.Fib(new FibArgs.Builder { Limit = 0 }.Build(), deadline: DateTime.UtcNow.AddMilliseconds(500))) { - var ex = Assert.Throws<RpcException>(async () => await call.ResponseStream.ToList()); + var ex = Assert.Throws<RpcException>(async () => await call.ResponseStream.ToListAsync()); // We can't guarantee the status code always DeadlineExceeded. See issue #2685. Assert.Contains(ex.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); @@ -167,7 +167,7 @@ namespace math.Tests var numbers = new List<long> { 10, 20, 30 }.ConvertAll( n => Num.CreateBuilder().SetNum_(n).Build()); - await call.RequestStream.WriteAll(numbers); + await call.RequestStream.WriteAllAsync(numbers); var result = await call.ResponseAsync; Assert.AreEqual(60, result.Num_); } @@ -185,8 +185,8 @@ namespace math.Tests using (var call = client.DivMany()) { - await call.RequestStream.WriteAll(divArgsList); - var result = await call.ResponseStream.ToList(); + await call.RequestStream.WriteAllAsync(divArgsList); + var result = await call.ResponseStream.ToListAsync(); CollectionAssert.AreEqual(new long[] { 3, 4, 3 }, result.ConvertAll((divReply) => divReply.Quotient)); CollectionAssert.AreEqual(new long[] { 1, 16, 1 }, result.ConvertAll((divReply) => divReply.Remainder)); diff --git a/src/csharp/Grpc.Examples/MathExamples.cs b/src/csharp/Grpc.Examples/MathExamples.cs index 06d81a4d83..dc1bf43995 100644 --- a/src/csharp/Grpc.Examples/MathExamples.cs +++ b/src/csharp/Grpc.Examples/MathExamples.cs @@ -54,7 +54,7 @@ namespace math { using (var call = client.Fib(new FibArgs.Builder { Limit = 5 }.Build())) { - List<Num> result = await call.ResponseStream.ToList(); + List<Num> result = await call.ResponseStream.ToListAsync(); Console.WriteLine("Fib Result: " + string.Join("|", result)); } } @@ -70,7 +70,7 @@ namespace math using (var call = client.Sum()) { - await call.RequestStream.WriteAll(numbers); + await call.RequestStream.WriteAllAsync(numbers); Console.WriteLine("Sum Result: " + await call.ResponseAsync); } } @@ -85,8 +85,8 @@ namespace math }; using (var call = client.DivMany()) { - await call.RequestStream.WriteAll(divArgsList); - Console.WriteLine("DivMany Result: " + string.Join("|", await call.ResponseStream.ToList())); + await call.RequestStream.WriteAllAsync(divArgsList); + Console.WriteLine("DivMany Result: " + string.Join("|", await call.ResponseStream.ToListAsync())); } } @@ -102,7 +102,7 @@ namespace math Num sum; using (var sumCall = client.Sum()) { - await sumCall.RequestStream.WriteAll(numbers); + await sumCall.RequestStream.WriteAllAsync(numbers); sum = await sumCall.ResponseAsync; } diff --git a/src/csharp/Grpc.Examples/MathServiceImpl.cs b/src/csharp/Grpc.Examples/MathServiceImpl.cs index dd26b1d350..7b2684615c 100644 --- a/src/csharp/Grpc.Examples/MathServiceImpl.cs +++ b/src/csharp/Grpc.Examples/MathServiceImpl.cs @@ -75,7 +75,7 @@ namespace math public async Task<Num> Sum(IAsyncStreamReader<Num> requestStream, ServerCallContext context) { long sum = 0; - await requestStream.ForEach(async num => + await requestStream.ForEachAsync(async num => { sum += num.Num_; }); @@ -84,10 +84,7 @@ namespace math public async Task DivMany(IAsyncStreamReader<DivArgs> requestStream, IServerStreamWriter<DivReply> responseStream, ServerCallContext context) { - await requestStream.ForEach(async divArgs => - { - await responseStream.WriteAsync(DivInternal(divArgs)); - }); + await requestStream.ForEachAsync(async divArgs => await responseStream.WriteAsync(DivInternal(divArgs))); } static DivReply DivInternal(DivArgs args) diff --git a/src/csharp/Grpc.HealthCheck.Tests/HealthServiceImplTest.cs b/src/csharp/Grpc.HealthCheck.Tests/HealthServiceImplTest.cs index 7184415655..c4caa3b57a 100644 --- a/src/csharp/Grpc.HealthCheck.Tests/HealthServiceImplTest.cs +++ b/src/csharp/Grpc.HealthCheck.Tests/HealthServiceImplTest.cs @@ -92,11 +92,11 @@ namespace Grpc.HealthCheck.Tests public void NullsRejected() { var impl = new HealthServiceImpl(); - Assert.Throws(typeof(NullReferenceException), () => impl.SetStatus(null, "", HealthCheckResponse.Types.ServingStatus.SERVING)); - Assert.Throws(typeof(NullReferenceException), () => impl.SetStatus("", null, HealthCheckResponse.Types.ServingStatus.SERVING)); + Assert.Throws(typeof(ArgumentNullException), () => impl.SetStatus(null, "", HealthCheckResponse.Types.ServingStatus.SERVING)); + Assert.Throws(typeof(ArgumentNullException), () => impl.SetStatus("", null, HealthCheckResponse.Types.ServingStatus.SERVING)); - Assert.Throws(typeof(NullReferenceException), () => impl.ClearStatus(null, "")); - Assert.Throws(typeof(NullReferenceException), () => impl.ClearStatus("", null)); + Assert.Throws(typeof(ArgumentNullException), () => impl.ClearStatus(null, "")); + Assert.Throws(typeof(ArgumentNullException), () => impl.ClearStatus("", null)); } private static HealthCheckResponse.Types.ServingStatus GetStatusHelper(HealthServiceImpl impl, string host, string service) diff --git a/src/csharp/Grpc.IntegrationTesting.Client/app.config b/src/csharp/Grpc.IntegrationTesting.Client/app.config index 0a82bb4f16..84d7534d65 100644 --- a/src/csharp/Grpc.IntegrationTesting.Client/app.config +++ b/src/csharp/Grpc.IntegrationTesting.Client/app.config @@ -10,6 +10,10 @@ <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.0.0.0" /> </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="Google.Apis.Core" publicKeyToken="4b01fa6e34db77ab" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-1.9.2.38523" newVersion="1.9.2.38523" /> + </dependentAssembly> </assemblyBinding> </runtime> </configuration>
\ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting.Server/app.config b/src/csharp/Grpc.IntegrationTesting.Server/app.config index 0a82bb4f16..84d7534d65 100644 --- a/src/csharp/Grpc.IntegrationTesting.Server/app.config +++ b/src/csharp/Grpc.IntegrationTesting.Server/app.config @@ -10,6 +10,10 @@ <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.0.0.0" /> </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="Google.Apis.Core" publicKeyToken="4b01fa6e34db77ab" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-1.9.2.38523" newVersion="1.9.2.38523" /> + </dependentAssembly> </assemblyBinding> </runtime> </configuration>
\ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj index 06a75a3351..2020a76d39 100644 --- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj +++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj @@ -8,7 +8,7 @@ <RootNamespace>Grpc.IntegrationTesting</RootNamespace> <AssemblyName>Grpc.IntegrationTesting</AssemblyName> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> - <NuGetPackageImportStamp>041c163e</NuGetPackageImportStamp> + <NuGetPackageImportStamp>6566287f</NuGetPackageImportStamp> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> @@ -38,20 +38,33 @@ <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> </PropertyGroup> <ItemGroup> - <Reference Include="BouncyCastle.Crypto"> + <Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath> </Reference> - <Reference Include="Google.Apis.Auth, Version=1.9.2.27817, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> + <Reference Include="Google.Apis.Auth, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath> + </Reference> + <Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.3.19383, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath> + </Reference> + <Reference Include="Google.Apis.Core, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath> + </Reference> + <Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\Google.Apis.Auth.1.9.2\lib\net40\Google.Apis.Auth.dll</HintPath> + <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath> </Reference> - <Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.2.27820, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> + <Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\Google.Apis.Auth.1.9.2\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath> + <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath> </Reference> - <Reference Include="Google.Apis.Core, Version=1.9.2.27816, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> + <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\Google.Apis.Core.1.9.2\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath> + <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath> </Reference> <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> @@ -78,15 +91,6 @@ <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath> </Reference> <Reference Include="System.Net.Http.WebRequest" /> - <Reference Include="Microsoft.Threading.Tasks"> - <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath> - </Reference> - <Reference Include="Microsoft.Threading.Tasks.Extensions"> - <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath> - </Reference> - <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop"> - <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath> - </Reference> </ItemGroup> <ItemGroup> <Compile Include="..\Grpc.Core\Version.cs"> diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs index 6802de489d..385ca92086 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs @@ -43,6 +43,7 @@ using Grpc.Auth; using Grpc.Core; using Grpc.Core.Utils; using NUnit.Framework; +using Google.Apis.Auth.OAuth2; namespace Grpc.IntegrationTesting { @@ -97,10 +98,10 @@ namespace Grpc.IntegrationTesting } var interopClient = new InteropClient(options); - interopClient.Run(); + interopClient.Run().Wait(); } - private void Run() + private async Task Run() { Credentials credentials = null; if (options.useTls) @@ -120,17 +121,7 @@ namespace Grpc.IntegrationTesting using (Channel channel = new Channel(options.serverHost, options.serverPort.Value, credentials, channelOptions)) { TestService.TestServiceClient client = new TestService.TestServiceClient(channel); - if (options.testCase == "service_account_creds" || options.testCase == "compute_engine_creds") - { - var credential = GoogleCredential.GetApplicationDefault(); - if (credential.IsCreateScopedRequired) - { - credential = credential.CreateScoped(new[] { AuthScope }); - } - client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential); - } - - RunTestCaseAsync(options.testCase, client).Wait(); + await RunTestCaseAsync(options.testCase, client); } GrpcEnvironment.Shutdown(); } @@ -158,16 +149,19 @@ namespace Grpc.IntegrationTesting await RunEmptyStreamAsync(client); break; case "service_account_creds": - RunServiceAccountCreds(client); + await RunServiceAccountCredsAsync(client); break; case "compute_engine_creds": - RunComputeEngineCreds(client); + await RunComputeEngineCredsAsync(client); + break; + case "jwt_token_creds": + await RunJwtTokenCredsAsync(client); break; case "oauth2_auth_token": - RunOAuth2AuthToken(client); + await RunOAuth2AuthTokenAsync(client); break; case "per_rpc_creds": - RunPerRpcCreds(client); + await RunPerRpcCredsAsync(client); break; case "cancel_after_begin": await RunCancelAfterBeginAsync(client); @@ -215,7 +209,7 @@ namespace Grpc.IntegrationTesting using (var call = client.StreamingInputCall()) { - await call.RequestStream.WriteAll(bodySizes); + await call.RequestStream.WriteAllAsync(bodySizes); var response = await call.ResponseAsync; Assert.AreEqual(74922, response.AggregatedPayloadSize); @@ -237,7 +231,7 @@ namespace Grpc.IntegrationTesting using (var call = client.StreamingOutputCall(request)) { - var responseList = await call.ResponseStream.ToList(); + var responseList = await call.ResponseStream.ToListAsync(); foreach (var res in responseList) { Assert.AreEqual(PayloadType.COMPRESSABLE, res.Payload.Type); @@ -303,15 +297,19 @@ namespace Grpc.IntegrationTesting { await call.RequestStream.CompleteAsync(); - var responseList = await call.ResponseStream.ToList(); + var responseList = await call.ResponseStream.ToListAsync(); Assert.AreEqual(0, responseList.Count); } Console.WriteLine("Passed!"); } - public static void RunServiceAccountCreds(TestService.ITestServiceClient client) + public static async Task RunServiceAccountCredsAsync(TestService.TestServiceClient client) { Console.WriteLine("running service_account_creds"); + var credential = await GoogleCredential.GetApplicationDefaultAsync(); + credential = credential.CreateScoped(new[] { AuthScope }); + client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential); + var request = SimpleRequest.CreateBuilder() .SetResponseType(PayloadType.COMPRESSABLE) .SetResponseSize(314159) @@ -329,9 +327,13 @@ namespace Grpc.IntegrationTesting Console.WriteLine("Passed!"); } - public static void RunComputeEngineCreds(TestService.ITestServiceClient client) + public static async Task RunComputeEngineCredsAsync(TestService.TestServiceClient client) { Console.WriteLine("running compute_engine_creds"); + var credential = await GoogleCredential.GetApplicationDefaultAsync(); + Assert.IsFalse(credential.IsCreateScopedRequired); + client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential); + var request = SimpleRequest.CreateBuilder() .SetResponseType(PayloadType.COMPRESSABLE) .SetResponseSize(314159) @@ -349,12 +351,35 @@ namespace Grpc.IntegrationTesting Console.WriteLine("Passed!"); } - public static void RunOAuth2AuthToken(TestService.TestServiceClient client) + public static async Task RunJwtTokenCredsAsync(TestService.TestServiceClient client) + { + Console.WriteLine("running jwt_token_creds"); + var credential = await GoogleCredential.GetApplicationDefaultAsync(); + // check this a credential with scope support, but don't add the scope. + Assert.IsTrue(credential.IsCreateScopedRequired); + client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential); + + var request = SimpleRequest.CreateBuilder() + .SetResponseType(PayloadType.COMPRESSABLE) + .SetResponseSize(314159) + .SetPayload(CreateZerosPayload(271828)) + .SetFillUsername(true) + .SetFillOauthScope(true) + .Build(); + + var response = client.UnaryCall(request); + + Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); + Assert.AreEqual(314159, response.Payload.Body.Length); + Assert.AreEqual(ServiceAccountUser, response.Username); + Console.WriteLine("Passed!"); + } + + public static async Task RunOAuth2AuthTokenAsync(TestService.TestServiceClient client) { Console.WriteLine("running oauth2_auth_token"); - var credential = GoogleCredential.GetApplicationDefault().CreateScoped(new[] { AuthScope }); - Assert.IsTrue(credential.RequestAccessTokenAsync(CancellationToken.None).Result); - string oauth2Token = credential.Token.AccessToken; + ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { AuthScope }); + string oauth2Token = await credential.GetAccessTokenForRequestAsync(); client.HeaderInterceptor = OAuth2Interceptors.FromAccessToken(oauth2Token); @@ -370,13 +395,12 @@ namespace Grpc.IntegrationTesting Console.WriteLine("Passed!"); } - public static void RunPerRpcCreds(TestService.TestServiceClient client) + public static async Task RunPerRpcCredsAsync(TestService.TestServiceClient client) { Console.WriteLine("running per_rpc_creds"); - var credential = GoogleCredential.GetApplicationDefault().CreateScoped(new[] { AuthScope }); - Assert.IsTrue(credential.RequestAccessTokenAsync(CancellationToken.None).Result); - string oauth2Token = credential.Token.AccessToken; + ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { AuthScope }); + string oauth2Token = await credential.GetAccessTokenForRequestAsync(); var headerInterceptor = OAuth2Interceptors.FromAccessToken(oauth2Token); var request = SimpleRequest.CreateBuilder() @@ -385,7 +409,7 @@ namespace Grpc.IntegrationTesting .Build(); var headers = new Metadata(); - headerInterceptor(headers); + headerInterceptor("", headers); var response = client.UnaryCall(request, headers: headers); Assert.AreEqual(AuthScopeResponse, response.OauthScope); diff --git a/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs b/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs index ccf9fe6ced..ceebd5dd8c 100644 --- a/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs +++ b/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs @@ -71,7 +71,7 @@ namespace grpc.testing public async Task<StreamingInputCallResponse> StreamingInputCall(IAsyncStreamReader<StreamingInputCallRequest> requestStream, ServerCallContext context) { int sum = 0; - await requestStream.ForEach(async request => + await requestStream.ForEachAsync(async request => { sum += request.Payload.Body.Length; }); @@ -80,7 +80,7 @@ namespace grpc.testing public async Task FullDuplexCall(IAsyncStreamReader<StreamingOutputCallRequest> requestStream, IServerStreamWriter<StreamingOutputCallResponse> responseStream, ServerCallContext context) { - await requestStream.ForEach(async request => + await requestStream.ForEachAsync(async request => { foreach (var responseParam in request.ResponseParametersList) { diff --git a/src/csharp/Grpc.IntegrationTesting/app.config b/src/csharp/Grpc.IntegrationTesting/app.config index 0a82bb4f16..84d7534d65 100644 --- a/src/csharp/Grpc.IntegrationTesting/app.config +++ b/src/csharp/Grpc.IntegrationTesting/app.config @@ -10,6 +10,10 @@ <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.0.0.0" /> </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="Google.Apis.Core" publicKeyToken="4b01fa6e34db77ab" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-1.9.2.38523" newVersion="1.9.2.38523" /> + </dependentAssembly> </assemblyBinding> </runtime> </configuration>
\ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting/packages.config b/src/csharp/Grpc.IntegrationTesting/packages.config index 7d1f84f303..0867b091b9 100644 --- a/src/csharp/Grpc.IntegrationTesting/packages.config +++ b/src/csharp/Grpc.IntegrationTesting/packages.config @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="BouncyCastle" version="1.7.0" targetFramework="net45" /> - <package id="Google.Apis.Auth" version="1.9.2" targetFramework="net45" /> - <package id="Google.Apis.Core" version="1.9.2" targetFramework="net45" /> + <package id="Google.Apis.Auth" version="1.9.3" targetFramework="net45" /> + <package id="Google.Apis.Core" version="1.9.3" targetFramework="net45" /> <package id="Google.ProtocolBuffers" version="2.4.1.521" targetFramework="net45" /> <package id="Ix-Async" version="1.2.3" targetFramework="net45" /> <package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" /> |