diff options
Diffstat (limited to 'src/csharp')
86 files changed, 3069 insertions, 988 deletions
diff --git a/src/csharp/Grpc.Auth/GoogleCredential.cs b/src/csharp/Grpc.Auth/GoogleCredential.cs index 7edf19ed67..9936cf583c 100644 --- a/src/csharp/Grpc.Auth/GoogleCredential.cs +++ b/src/csharp/Grpc.Auth/GoogleCredential.cs @@ -89,17 +89,15 @@ namespace Grpc.Auth return new GoogleCredential(new ComputeCredential(new ComputeCredential.Initializer())); } - JObject o1 = JObject.Parse(File.ReadAllText(credsPath)); - string clientEmail = o1.GetValue(ClientEmailFieldName).Value<string>(); - string privateKeyString = o1.GetValue(PrivateKeyFieldName).Value<string>(); - var privateKey = ParsePrivateKeyFromString(privateKeyString); + 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, - Key = privateKey - }); + }.FromPrivateKey(privateKeyString)); return new GoogleCredential(serviceCredential); } @@ -123,16 +121,5 @@ namespace Grpc.Auth return credential; } } - - private RSACryptoServiceProvider ParsePrivateKeyFromString(string base64PrivateKey) - { - // TODO(jtattermusch): temporary code to create RSACryptoServiceProvider. - base64PrivateKey = base64PrivateKey.Replace("-----BEGIN PRIVATE KEY-----", "").Replace("\n", "").Replace("-----END PRIVATE KEY-----", ""); - RsaPrivateCrtKeyParameters key = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(base64PrivateKey)); - RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(key); - RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); - rsa.ImportParameters(rsaParameters); - return rsa; - } } } diff --git a/src/csharp/Grpc.Auth/Grpc.Auth.csproj b/src/csharp/Grpc.Auth/Grpc.Auth.csproj index fdec2e7bd7..8e5036832d 100644 --- a/src/csharp/Grpc.Auth/Grpc.Auth.csproj +++ b/src/csharp/Grpc.Auth/Grpc.Auth.csproj @@ -11,6 +11,7 @@ <AssemblyName>Grpc.Auth</AssemblyName> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <DocumentationFile>bin\$(Configuration)\Grpc.Auth.Xml</DocumentationFile> + <NuGetPackageImportStamp>9b408026</NuGetPackageImportStamp> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> @@ -23,25 +24,37 @@ <ConsolePause>false</ConsolePause> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> - <DebugType>full</DebugType> + <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <ConsolePause>false</ConsolePause> </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\ReleaseSigned</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> + </PropertyGroup> <ItemGroup> <Reference Include="BouncyCastle.Crypto"> <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath> </Reference> - <Reference Include="Google.Apis.Auth"> - <HintPath>..\packages\Google.Apis.Auth.1.9.1\lib\net40\Google.Apis.Auth.dll</HintPath> + <Reference Include="Google.Apis.Auth, Version=1.9.2.27817, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Google.Apis.Auth.1.9.2\lib\net40\Google.Apis.Auth.dll</HintPath> </Reference> - <Reference Include="Google.Apis.Auth.PlatformServices"> - <HintPath>..\packages\Google.Apis.Auth.1.9.1\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath> + <Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.2.27820, 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> </Reference> - <Reference Include="Google.Apis.Core"> - <HintPath>..\packages\Google.Apis.Core.1.9.1\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath> + <Reference Include="Google.Apis.Core, Version=1.9.2.27816, 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> </Reference> <Reference Include="Microsoft.Threading.Tasks"> <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath> @@ -52,18 +65,20 @@ <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> - <Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> + <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\Newtonsoft.Json.6.0.6\lib\net45\Newtonsoft.Json.dll</HintPath> + <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath> </Reference> <Reference Include="System" /> <Reference Include="System.Net" /> <Reference Include="System.Net.Http" /> - <Reference Include="System.Net.Http.Extensions"> - <HintPath>..\packages\Microsoft.Net.Http.2.2.28\lib\net45\System.Net.Http.Extensions.dll</HintPath> + <Reference Include="System.Net.Http.Extensions, Version=2.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath> </Reference> - <Reference Include="System.Net.Http.Primitives"> - <HintPath>..\packages\Microsoft.Net.Http.2.2.28\lib\net45\System.Net.Http.Primitives.dll</HintPath> + <Reference Include="System.Net.Http.Primitives, Version=4.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath> </Reference> <Reference Include="System.Net.Http.WebRequest" /> </ItemGroup> @@ -73,7 +88,7 @@ </Compile> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="GoogleCredential.cs" /> - <Compile Include="OAuth2InterceptorFactory.cs" /> + <Compile Include="OAuth2Interceptors.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup> @@ -87,9 +102,11 @@ <None Include="Grpc.Auth.nuspec" /> <None Include="packages.config" /> </ItemGroup> - <Import Project="..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" /> - <Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''"> - <Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" /> - <Error Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" /> + <Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" /> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" /> </Target> </Project>
\ No newline at end of file diff --git a/src/csharp/Grpc.Auth/Grpc.Auth.nuspec b/src/csharp/Grpc.Auth/Grpc.Auth.nuspec index 1262bdbdab..2dc10d24c2 100644 --- a/src/csharp/Grpc.Auth/Grpc.Auth.nuspec +++ b/src/csharp/Grpc.Auth/Grpc.Auth.nuspec @@ -15,15 +15,14 @@ <copyright>Copyright 2015, Google Inc.</copyright> <tags>gRPC RPC Protocol HTTP/2 Auth OAuth2</tags> <dependencies> - <dependency id="BouncyCastle" version="1.7.0" /> - <dependency id="Google.Apis.Auth" version="1.9.1" /> + <dependency id="Google.Apis.Auth" version="1.9.2" /> <dependency id="Grpc.Core" version="$version$" /> </dependencies> </metadata> <files> - <file src="bin/Release/Grpc.Auth.dll" target="lib/net45" /> - <file src="bin/Release/Grpc.Auth.pdb" target="lib/net45" /> - <file src="bin/Release/Grpc.Auth.xml" target="lib/net45" /> + <file src="bin/ReleaseSigned/Grpc.Auth.dll" target="lib/net45" /> + <file src="bin/ReleaseSigned/Grpc.Auth.pdb" target="lib/net45" /> + <file src="bin/ReleaseSigned/Grpc.Auth.xml" target="lib/net45" /> <file src="**\*.cs" target="src" /> </files> </package> diff --git a/src/csharp/Grpc.Auth/OAuth2InterceptorFactory.cs b/src/csharp/Grpc.Auth/OAuth2Interceptors.cs index 420c4cb537..cc9d2c175f 100644 --- a/src/csharp/Grpc.Auth/OAuth2InterceptorFactory.cs +++ b/src/csharp/Grpc.Auth/OAuth2Interceptors.cs @@ -47,18 +47,32 @@ using Grpc.Core.Utils; namespace Grpc.Auth { - public static class OAuth2InterceptorFactory + public static class OAuth2Interceptors { /// <summary> - /// Creates OAuth2 interceptor. + /// Creates OAuth2 interceptor that will obtain access token from GoogleCredentials. /// </summary> - public static MetadataInterceptorDelegate Create(GoogleCredential googleCredential) + public static MetadataInterceptorDelegate FromCredential(GoogleCredential googleCredential) { var interceptor = new OAuth2Interceptor(googleCredential.InternalCredential, SystemClock.Default); return new MetadataInterceptorDelegate(interceptor.InterceptHeaders); } /// <summary> + /// Creates OAuth2 interceptor that will use given OAuth2 token. + /// </summary> + /// <param name="oauth2Token"></param> + /// <returns></returns> + public static MetadataInterceptorDelegate FromAccessToken(string oauth2Token) + { + Preconditions.CheckNotNull(oauth2Token); + return new MetadataInterceptorDelegate((metadata) => + { + metadata.Add(OAuth2Interceptor.CreateBearerTokenHeader(oauth2Token)); + }); + } + + /// <summary> /// Injects OAuth2 authorization header into initial metadata (= request headers). /// </summary> private class OAuth2Interceptor @@ -97,7 +111,12 @@ namespace Grpc.Auth public void InterceptHeaders(Metadata metadata) { var accessToken = GetAccessToken(CancellationToken.None); - metadata.Add(new Metadata.Entry(AuthorizationHeader, Schema + " " + accessToken)); + metadata.Add(CreateBearerTokenHeader(accessToken)); + } + + public static Metadata.Entry CreateBearerTokenHeader(string accessToken) + { + return new Metadata.Entry(AuthorizationHeader, Schema + " " + accessToken); } } } diff --git a/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs index 70cb32d5b2..83396c546b 100644 --- a/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs @@ -9,5 +9,3 @@ using System.Runtime.CompilerServices; [assembly: AssemblyCopyright("Google Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] - -[assembly: InternalsVisibleTo("Grpc.Auth.Tests")] diff --git a/src/csharp/Grpc.Auth/app.config b/src/csharp/Grpc.Auth/app.config index 966b777192..0a82bb4f16 100644 --- a/src/csharp/Grpc.Auth/app.config +++ b/src/csharp/Grpc.Auth/app.config @@ -4,7 +4,7 @@ <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.2.28.0" /> + <bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> diff --git a/src/csharp/Grpc.Auth/packages.config b/src/csharp/Grpc.Auth/packages.config index 7d348872ba..29be953bf3 100644 --- a/src/csharp/Grpc.Auth/packages.config +++ b/src/csharp/Grpc.Auth/packages.config @@ -1,11 +1,11 @@ <?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.1" targetFramework="net45" /> - <package id="Google.Apis.Core" version="1.9.1" targetFramework="net45" /> - <package id="Microsoft.Bcl" version="1.1.9" 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="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.14" targetFramework="net45" /> - <package id="Microsoft.Net.Http" version="2.2.28" targetFramework="net45" /> - <package id="Newtonsoft.Json" version="6.0.6" targetFramework="net45" /> + <package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" /> + <package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" /> + <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/src/csharp/Grpc.Core.Tests/TimespecTest.cs b/src/csharp/Grpc.Core.Tests/ChannelTest.cs index a34b407a01..60b45176e5 100644 --- a/src/csharp/Grpc.Core.Tests/TimespecTest.cs +++ b/src/csharp/Grpc.Core.Tests/ChannelTest.cs @@ -32,70 +32,60 @@ #endregion using System; -using System.Runtime.InteropServices; +using Grpc.Core; using Grpc.Core.Internal; +using Grpc.Core.Utils; using NUnit.Framework; -namespace Grpc.Core.Internal.Tests +namespace Grpc.Core.Tests { - public class TimespecTest + public class ChannelTest { - [Test] - public void Now() + [TestFixtureTearDown] + public void CleanupClass() { - var timespec = Timespec.Now; + GrpcEnvironment.Shutdown(); } [Test] - public void InfFuture() + public void Constructor_RejectsInvalidParams() { - var timespec = Timespec.InfFuture; + Assert.Throws(typeof(NullReferenceException), () => new Channel(null, Credentials.Insecure)); } [Test] - public void TimespecSizeIsNativeSize() + public void State_IdleAfterCreation() { - Assert.AreEqual(Timespec.NativeSize, Marshal.SizeOf(typeof(Timespec))); - } - - [Test] - public void ToDateTime() - { - Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), - new Timespec(IntPtr.Zero, 0).ToDateTime()); - - Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 10, DateTimeKind.Utc).AddTicks(50), - new Timespec(new IntPtr(10), 5000).ToDateTime()); - - Assert.AreEqual(new DateTime(2015, 7, 21, 4, 21, 48, DateTimeKind.Utc), - new Timespec(new IntPtr(1437452508), 0).ToDateTime()); + using (var channel = new Channel("localhost", Credentials.Insecure)) + { + Assert.AreEqual(ChannelState.Idle, channel.State); + } } [Test] - public void Add() + public void WaitForStateChangedAsync_InvalidArgument() { - var t = new Timespec { tv_sec = new IntPtr(12345), tv_nsec = 123456789 }; - var result = t.Add(TimeSpan.FromTicks(TimeSpan.TicksPerSecond * 10)); - Assert.AreEqual(result.tv_sec, new IntPtr(12355)); - Assert.AreEqual(result.tv_nsec, 123456789); + using (var channel = new Channel("localhost", Credentials.Insecure)) + { + Assert.Throws(typeof(ArgumentException), () => channel.WaitForStateChangedAsync(ChannelState.FatalFailure)); + } } [Test] - public void Add_Nanos() + public void Target() { - var t = new Timespec { tv_sec = new IntPtr(12345), tv_nsec = 123456789 }; - var result = t.Add(TimeSpan.FromTicks(10)); - Assert.AreEqual(result.tv_sec, new IntPtr(12345)); - Assert.AreEqual(result.tv_nsec, 123456789 + 1000); + using (var channel = new Channel("127.0.0.1", Credentials.Insecure)) + { + Assert.IsTrue(channel.Target.Contains("127.0.0.1")); + } } [Test] - public void Add_NanosOverflow() + public void Dispose_IsIdempotent() { - var t = new Timespec { tv_sec = new IntPtr(12345), tv_nsec = 999999999 }; - var result = t.Add(TimeSpan.FromTicks(TimeSpan.TicksPerSecond * 10 + 10)); - Assert.AreEqual(result.tv_sec, new IntPtr(12356)); - Assert.AreEqual(result.tv_nsec, 999); + var channel = new Channel("localhost", Credentials.Insecure); + channel.Dispose(); + channel.Dispose(); } } } diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index 8ba2c8a9a2..64ea21800f 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -45,24 +45,27 @@ namespace Grpc.Core.Tests { public class ClientServerTest { - const string Host = "localhost"; - const string ServiceName = "/tests.Test"; + const string Host = "127.0.0.1"; + const string ServiceName = "tests.Test"; static readonly Method<string, string> EchoMethod = new Method<string, string>( MethodType.Unary, - "/tests.Test/Echo", + ServiceName, + "Echo", Marshallers.StringMarshaller, Marshallers.StringMarshaller); static readonly Method<string, string> ConcatAndEchoMethod = new Method<string, string>( MethodType.ClientStreaming, - "/tests.Test/ConcatAndEcho", + ServiceName, + "ConcatAndEcho", Marshallers.StringMarshaller, Marshallers.StringMarshaller); static readonly Method<string, string> NonexistentMethod = new Method<string, string>( MethodType.Unary, - "/tests.Test/NonexistentMethod", + ServiceName, + "NonexistentMethod", Marshallers.StringMarshaller, Marshallers.StringMarshaller); @@ -77,11 +80,13 @@ namespace Grpc.Core.Tests [SetUp] public void Init() { - server = new Server(); - server.AddServiceDefinition(ServiceDefinition); - int port = server.AddListeningPort(Host, Server.PickUnusedPort); + server = new Server + { + Services = { ServiceDefinition }, + Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } + }; server.Start(); - channel = new Channel(Host, port); + channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure); } [TearDown] @@ -100,17 +105,17 @@ namespace Grpc.Core.Tests [Test] public void UnaryCall() { - var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty); - Assert.AreEqual("ABC", Calls.BlockingUnaryCall(internalCall, "ABC", CancellationToken.None)); + var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions()); + Assert.AreEqual("ABC", Calls.BlockingUnaryCall(callDetails, "ABC")); } [Test] public void UnaryCall_ServerHandlerThrows() { - var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty); + var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions()); try { - Calls.BlockingUnaryCall(internalCall, "THROW", CancellationToken.None); + Calls.BlockingUnaryCall(callDetails, "THROW"); Assert.Fail(); } catch (RpcException e) @@ -122,10 +127,10 @@ namespace Grpc.Core.Tests [Test] public void UnaryCall_ServerHandlerThrowsRpcException() { - var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty); + var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions()); try { - Calls.BlockingUnaryCall(internalCall, "THROW_UNAUTHENTICATED", CancellationToken.None); + Calls.BlockingUnaryCall(callDetails, "THROW_UNAUTHENTICATED"); Assert.Fail(); } catch (RpcException e) @@ -137,10 +142,10 @@ namespace Grpc.Core.Tests [Test] public void UnaryCall_ServerHandlerSetsStatus() { - var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty); + var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions()); try { - Calls.BlockingUnaryCall(internalCall, "SET_UNAUTHENTICATED", CancellationToken.None); + Calls.BlockingUnaryCall(callDetails, "SET_UNAUTHENTICATED"); Assert.Fail(); } catch (RpcException e) @@ -150,67 +155,57 @@ namespace Grpc.Core.Tests } [Test] - public void AsyncUnaryCall() + public async Task AsyncUnaryCall() { - var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty); - var result = Calls.AsyncUnaryCall(internalCall, "ABC", CancellationToken.None).ResponseAsync.Result; + var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions()); + var result = await Calls.AsyncUnaryCall(callDetails, "ABC"); Assert.AreEqual("ABC", result); } [Test] - public void AsyncUnaryCall_ServerHandlerThrows() + public async Task AsyncUnaryCall_ServerHandlerThrows() { - Task.Run(async () => + var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions()); + try { - var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty); - try - { - await Calls.AsyncUnaryCall(internalCall, "THROW", CancellationToken.None); - Assert.Fail(); - } - catch (RpcException e) - { - Assert.AreEqual(StatusCode.Unknown, e.Status.StatusCode); - } - }).Wait(); + await Calls.AsyncUnaryCall(callDetails, "THROW"); + Assert.Fail(); + } + catch (RpcException e) + { + Assert.AreEqual(StatusCode.Unknown, e.Status.StatusCode); + } } [Test] - public void ClientStreamingCall() + public async Task ClientStreamingCall() { - Task.Run(async () => - { - var internalCall = new Call<string, string>(ServiceName, ConcatAndEchoMethod, channel, Metadata.Empty); - var call = Calls.AsyncClientStreamingCall(internalCall, CancellationToken.None); + var callDetails = new CallInvocationDetails<string, string>(channel, ConcatAndEchoMethod, new CallOptions()); + var call = Calls.AsyncClientStreamingCall(callDetails); - await call.RequestStream.WriteAll(new string[] { "A", "B", "C" }); - Assert.AreEqual("ABC", await call.ResponseAsync); - }).Wait(); + await call.RequestStream.WriteAll(new string[] { "A", "B", "C" }); + Assert.AreEqual("ABC", await call.ResponseAsync); } [Test] - public void ClientStreamingCall_CancelAfterBegin() + public async Task ClientStreamingCall_CancelAfterBegin() { - Task.Run(async () => - { - var internalCall = new Call<string, string>(ServiceName, ConcatAndEchoMethod, channel, Metadata.Empty); + var cts = new CancellationTokenSource(); + var callDetails = new CallInvocationDetails<string, string>(channel, ConcatAndEchoMethod, new CallOptions(cancellationToken: cts.Token)); + var call = Calls.AsyncClientStreamingCall(callDetails); - var cts = new CancellationTokenSource(); - var call = Calls.AsyncClientStreamingCall(internalCall, cts.Token); + // TODO(jtattermusch): we need this to ensure call has been initiated once we cancel it. + await Task.Delay(1000); + cts.Cancel(); - // TODO(jtattermusch): we need this to ensure call has been initiated once we cancel it. - await Task.Delay(1000); - cts.Cancel(); - - try - { - await call.ResponseAsync; - } - catch (RpcException e) - { - Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode); - } - }).Wait(); + try + { + await call.ResponseAsync; + } + catch (RpcException e) + { + Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode); + } } [Test] @@ -218,11 +213,11 @@ namespace Grpc.Core.Tests { var headers = new Metadata { - new Metadata.Entry("asciiHeader", "abcdefg"), - new Metadata.Entry("binaryHeader-bin", new byte[] { 1, 2, 3, 0, 0xff }), + new Metadata.Entry("ascii-header", "abcdefg"), + new Metadata.Entry("binary-header-bin", new byte[] { 1, 2, 3, 0, 0xff }), }; - var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, headers); - var call = Calls.AsyncUnaryCall(internalCall, "ABC", CancellationToken.None); + var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions(headers: headers)); + var call = Calls.AsyncUnaryCall(callDetails, "ABC"); Assert.AreEqual("ABC", call.ResponseAsync.Result); @@ -242,25 +237,25 @@ namespace Grpc.Core.Tests { channel.Dispose(); - var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty); - Assert.Throws(typeof(ObjectDisposedException), () => Calls.BlockingUnaryCall(internalCall, "ABC", CancellationToken.None)); + var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions()); + Assert.Throws(typeof(ObjectDisposedException), () => Calls.BlockingUnaryCall(callDetails, "ABC")); } [Test] public void UnaryCallPerformance() { - var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty); + var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions()); BenchmarkUtil.RunBenchmark(100, 100, - () => { Calls.BlockingUnaryCall(internalCall, "ABC", default(CancellationToken)); }); + () => { Calls.BlockingUnaryCall(callDetails, "ABC"); }); } [Test] public void UnknownMethodHandler() { - var internalCall = new Call<string, string>(ServiceName, NonexistentMethod, channel, Metadata.Empty); + var callDetails = new CallInvocationDetails<string, string>(channel, NonexistentMethod, new CallOptions()); try { - Calls.BlockingUnaryCall(internalCall, "ABC", default(CancellationToken)); + Calls.BlockingUnaryCall(callDetails, "ABC"); Assert.Fail(); } catch (RpcException e) @@ -272,11 +267,43 @@ namespace Grpc.Core.Tests [Test] public void UserAgentStringPresent() { - var internalCall = new Call<string, string>(ServiceName, EchoMethod, channel, Metadata.Empty); - string userAgent = Calls.BlockingUnaryCall(internalCall, "RETURN-USER-AGENT", CancellationToken.None); + var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions()); + string userAgent = Calls.BlockingUnaryCall(callDetails, "RETURN-USER-AGENT"); Assert.IsTrue(userAgent.StartsWith("grpc-csharp/")); } + [Test] + public void PeerInfoPresent() + { + var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions()); + string peer = Calls.BlockingUnaryCall(callDetails, "RETURN-PEER"); + Assert.IsTrue(peer.Contains(Host)); + } + + [Test] + public async Task Channel_WaitForStateChangedAsync() + { + Assert.Throws(typeof(TaskCanceledException), + async () => await channel.WaitForStateChangedAsync(channel.State, DateTime.UtcNow.AddMilliseconds(10))); + + var stateChangedTask = channel.WaitForStateChangedAsync(channel.State); + + var callDetails = new CallInvocationDetails<string, string>(channel, EchoMethod, new CallOptions()); + await Calls.AsyncUnaryCall(callDetails, "abc"); + + await stateChangedTask; + Assert.AreEqual(ChannelState.Ready, channel.State); + } + + [Test] + public async Task Channel_ConnectAsync() + { + await channel.ConnectAsync(); + Assert.AreEqual(ChannelState.Ready, channel.State); + await channel.ConnectAsync(DateTime.UtcNow.AddMilliseconds(1000)); + Assert.AreEqual(ChannelState.Ready, channel.State); + } + private static async Task<string> EchoHandler(string request, ServerCallContext context) { foreach (Metadata.Entry metadataEntry in context.RequestHeaders) @@ -292,6 +319,11 @@ namespace Grpc.Core.Tests return context.RequestHeaders.Where(entry => entry.Key == "user-agent").Single().Value; } + if (request == "RETURN-PEER") + { + return context.Peer; + } + if (request == "THROW") { throw new Exception("This was thrown on purpose by a test"); diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj index 927954c448..f2bf459dc5 100644 --- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj +++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj @@ -17,20 +17,43 @@ <DefineConstants>DEBUG;</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <ConsolePause>false</ConsolePause> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> - <DebugType>full</DebugType> + <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <ConsolePause>false</ConsolePause> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\ReleaseSigned</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> </PropertyGroup> <ItemGroup> + <Reference Include="nunit.core"> + <HintPath>..\packages\NUnitTestAdapter.2.0.0\lib\nunit.core.dll</HintPath> + <Private>False</Private> + </Reference> + <Reference Include="nunit.core.interfaces"> + <HintPath>..\packages\NUnitTestAdapter.2.0.0\lib\nunit.core.interfaces.dll</HintPath> + <Private>False</Private> + </Reference> <Reference Include="nunit.framework"> <HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath> </Reference> + <Reference Include="nunit.util"> + <HintPath>..\packages\NUnitTestAdapter.2.0.0\lib\nunit.util.dll</HintPath> + <Private>False</Private> + </Reference> + <Reference Include="NUnit.VisualStudio.TestAdapter"> + <HintPath>..\packages\NUnitTestAdapter.2.0.0\lib\NUnit.VisualStudio.TestAdapter.dll</HintPath> + <Private>False</Private> + </Reference> <Reference Include="System" /> <Reference Include="System.Interactive.Async"> <HintPath>..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll</HintPath> @@ -44,13 +67,16 @@ <Compile Include="ClientServerTest.cs" /> <Compile Include="ServerTest.cs" /> <Compile Include="GrpcEnvironmentTest.cs" /> - <Compile Include="TimespecTest.cs" /> <Compile Include="PInvokeTest.cs" /> <Compile Include="Internal\MetadataArraySafeHandleTest.cs" /> <Compile Include="Internal\CompletionQueueSafeHandleTest.cs" /> <Compile Include="Internal\CompletionQueueEventTest.cs" /> <Compile Include="Internal\ChannelArgsSafeHandleTest.cs" /> <Compile Include="ChannelOptionsTest.cs" /> + <Compile Include="Internal\TimespecTest.cs" /> + <Compile Include="TimeoutsTest.cs" /> + <Compile Include="NUnitVersionTest.cs" /> + <Compile Include="ChannelTest.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup> @@ -60,7 +86,9 @@ </ProjectReference> </ItemGroup> <ItemGroup> - <None Include="packages.config" /> + <None Include="packages.config"> + <SubType>Designer</SubType> + </None> </ItemGroup> <ItemGroup> <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> diff --git a/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs b/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs new file mode 100644 index 0000000000..874df02baa --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs @@ -0,0 +1,202 @@ +#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.Runtime.InteropServices; +using Grpc.Core.Internal; +using NUnit.Framework; + +namespace Grpc.Core.Internal.Tests +{ + public class TimespecTest + { + [Test] + public void Now_IsInUtc() + { + Assert.AreEqual(DateTimeKind.Utc, Timespec.Now.ToDateTime().Kind); + } + + [Test] + public void Now_AgreesWithUtcNow() + { + var timespec = Timespec.Now; + var utcNow = DateTime.UtcNow; + + TimeSpan difference = utcNow - timespec.ToDateTime(); + + // This test is inherently a race - but the two timestamps + // should really be way less that a minute apart. + Assert.IsTrue(difference.TotalSeconds < 60); + } + + [Test] + public void InfFuture() + { + var timespec = Timespec.InfFuture; + } + + [Test] + public void InfPast() + { + var timespec = Timespec.InfPast; + } + + [Test] + public void TimespecSizeIsNativeSize() + { + Assert.AreEqual(Timespec.NativeSize, Marshal.SizeOf(typeof(Timespec))); + } + + [Test] + public void ToDateTime() + { + Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), + new Timespec(IntPtr.Zero, 0).ToDateTime()); + + Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 10, DateTimeKind.Utc).AddTicks(50), + new Timespec(new IntPtr(10), 5000).ToDateTime()); + + Assert.AreEqual(new DateTime(2015, 7, 21, 4, 21, 48, DateTimeKind.Utc), + new Timespec(new IntPtr(1437452508), 0).ToDateTime()); + + // before epoch + Assert.AreEqual(new DateTime(1969, 12, 31, 23, 59, 55, DateTimeKind.Utc).AddTicks(10), + new Timespec(new IntPtr(-5), 1000).ToDateTime()); + + // infinity + Assert.AreEqual(DateTime.MaxValue, Timespec.InfFuture.ToDateTime()); + Assert.AreEqual(DateTime.MinValue, Timespec.InfPast.ToDateTime()); + + // nanos are rounded to ticks are rounded up + Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddTicks(1), + new Timespec(IntPtr.Zero, 99).ToDateTime()); + + // Illegal inputs + Assert.Throws(typeof(InvalidOperationException), + () => new Timespec(new IntPtr(0), -2).ToDateTime()); + Assert.Throws(typeof(InvalidOperationException), + () => new Timespec(new IntPtr(0), 1000 * 1000 * 1000).ToDateTime()); + Assert.Throws(typeof(InvalidOperationException), + () => new Timespec(new IntPtr(0), 0, GPRClockType.Monotonic).ToDateTime()); + } + + [Test] + public void ToDateTime_ReturnsUtc() + { + Assert.AreEqual(DateTimeKind.Utc, new Timespec(new IntPtr(1437452508), 0).ToDateTime().Kind); + Assert.AreNotEqual(DateTimeKind.Unspecified, new Timespec(new IntPtr(1437452508), 0).ToDateTime().Kind); + } + + [Test] + public void ToDateTime_Overflow() + { + // we can only get overflow in ticks arithmetic on 64-bit + if (IntPtr.Size == 8) + { + var timespec = new Timespec(new IntPtr(long.MaxValue - 100), 0); + Assert.AreNotEqual(Timespec.InfFuture, timespec); + Assert.AreEqual(DateTime.MaxValue, timespec.ToDateTime()); + + Assert.AreEqual(DateTime.MinValue, new Timespec(new IntPtr(long.MinValue + 100), 0).ToDateTime()); + } + else + { + Console.WriteLine("Test cannot be run on this platform, skipping the test."); + } + } + + [Test] + public void ToDateTime_OutOfDateTimeRange() + { + // we can only get out of range on 64-bit, on 32 bit the max + // timestamp is ~ Jan 19 2038, which is far within range of DateTime + // same case for min value. + if (IntPtr.Size == 8) + { + // DateTime range goes up to year 9999, 20000 years from now should + // be out of range. + long seconds = 20000L * 365L * 24L * 3600L; + + var timespec = new Timespec(new IntPtr(seconds), 0); + Assert.AreNotEqual(Timespec.InfFuture, timespec); + Assert.AreEqual(DateTime.MaxValue, timespec.ToDateTime()); + + Assert.AreEqual(DateTime.MinValue, new Timespec(new IntPtr(-seconds), 0).ToDateTime()); + } + else + { + Console.WriteLine("Test cannot be run on this platform, skipping the test"); + } + } + + [Test] + public void FromDateTime() + { + Assert.AreEqual(new Timespec(IntPtr.Zero, 0), + Timespec.FromDateTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc))); + + Assert.AreEqual(new Timespec(new IntPtr(10), 5000), + Timespec.FromDateTime(new DateTime(1970, 1, 1, 0, 0, 10, DateTimeKind.Utc).AddTicks(50))); + + Assert.AreEqual(new Timespec(new IntPtr(1437452508), 0), + Timespec.FromDateTime(new DateTime(2015, 7, 21, 4, 21, 48, DateTimeKind.Utc))); + + // before epoch + Assert.AreEqual(new Timespec(new IntPtr(-5), 1000), + Timespec.FromDateTime(new DateTime(1969, 12, 31, 23, 59, 55, DateTimeKind.Utc).AddTicks(10))); + + // infinity + Assert.AreEqual(Timespec.InfFuture, Timespec.FromDateTime(DateTime.MaxValue)); + Assert.AreEqual(Timespec.InfPast, Timespec.FromDateTime(DateTime.MinValue)); + + // illegal inputs + Assert.Throws(typeof(ArgumentException), + () => Timespec.FromDateTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Unspecified))); + } + + [Test] + public void FromDateTime_OutOfTimespecRange() + { + // we can only get overflow in Timespec on 32-bit + if (IntPtr.Size == 4) + { + Assert.AreEqual(Timespec.InfFuture, Timespec.FromDateTime(new DateTime(2040, 1, 1, 0, 0, 0, DateTimeKind.Utc))); + Assert.AreEqual(Timespec.InfPast, Timespec.FromDateTime(new DateTime(1800, 1, 1, 0, 0, 0, DateTimeKind.Utc))); + } + else + { + Console.WriteLine("Test cannot be run on this platform, skipping the test."); + } + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/NUnitVersionTest.cs b/src/csharp/Grpc.Core.Tests/NUnitVersionTest.cs new file mode 100644 index 0000000000..3fa6ad09c0 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/NUnitVersionTest.cs @@ -0,0 +1,77 @@ +#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.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + /// <summary> + /// Tests if the version of nunit-console used is sufficient to run async tests. + /// </summary> + public class NUnitVersionTest + { + private int testRunCount = 0; + + [TestFixtureTearDown] + public void Cleanup() + { + if (testRunCount != 2) + { + Console.Error.WriteLine("You are using and old version of NUnit that doesn't support async tests and skips them instead. " + + "This test has failed to indicate that."); + Console.Error.Flush(); + Environment.Exit(1); + } + } + + [Test] + public void NUnitVersionTest1() + { + testRunCount++; + } + + // Old version of NUnit will skip this test + [Test] + public async Task NUnitVersionTest2() + { + testRunCount++; + await Task.Delay(10); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/ServerTest.cs b/src/csharp/Grpc.Core.Tests/ServerTest.cs index 1119aa370e..485006ebac 100644 --- a/src/csharp/Grpc.Core.Tests/ServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ServerTest.cs @@ -32,6 +32,7 @@ #endregion using System; +using System.Linq; using Grpc.Core; using Grpc.Core.Internal; using Grpc.Core.Utils; @@ -44,11 +45,45 @@ namespace Grpc.Core.Tests [Test] public void StartAndShutdownServer() { - Server server = new Server(); - server.AddListeningPort("localhost", Server.PickUnusedPort); + Server server = new Server + { + Ports = { new ServerPort("localhost", ServerPort.PickUnused, ServerCredentials.Insecure) } + }; server.Start(); server.ShutdownAsync().Wait(); GrpcEnvironment.Shutdown(); } + + [Test] + public void PickUnusedPort() + { + Server server = new Server + { + Ports = { new ServerPort("localhost", ServerPort.PickUnused, ServerCredentials.Insecure) } + }; + + var boundPort = server.Ports.Single(); + Assert.AreEqual(0, boundPort.Port); + Assert.Greater(boundPort.BoundPort, 0); + + server.Start(); + server.ShutdownAsync(); + GrpcEnvironment.Shutdown(); + } + + [Test] + public void CannotModifyAfterStarted() + { + Server server = new Server + { + Ports = { new ServerPort("localhost", ServerPort.PickUnused, ServerCredentials.Insecure) } + }; + server.Start(); + Assert.Throws(typeof(InvalidOperationException), () => server.Ports.Add("localhost", 9999, ServerCredentials.Insecure)); + Assert.Throws(typeof(InvalidOperationException), () => server.Services.Add(ServerServiceDefinition.CreateBuilder("serviceName").Build())); + + server.ShutdownAsync().Wait(); + GrpcEnvironment.Shutdown(); + } } } diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs new file mode 100644 index 0000000000..fc395b0acd --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs @@ -0,0 +1,212 @@ +#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.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + /// <summary> + /// Tests for Deadline support. + /// </summary> + public class TimeoutsTest + { + const string Host = "localhost"; + const string ServiceName = "tests.Test"; + + static readonly Method<string, string> TestMethod = new Method<string, string>( + MethodType.Unary, + ServiceName, + "Test", + Marshallers.StringMarshaller, + Marshallers.StringMarshaller); + + static readonly ServerServiceDefinition ServiceDefinition = ServerServiceDefinition.CreateBuilder(ServiceName) + .AddMethod(TestMethod, TestMethodHandler) + .Build(); + + // provides a way how to retrieve an out-of-band result value from server handler + static TaskCompletionSource<string> stringFromServerHandlerTcs; + + Server server; + Channel channel; + + [SetUp] + public void Init() + { + server = new Server + { + Services = { ServiceDefinition }, + Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } + }; + server.Start(); + channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure); + + stringFromServerHandlerTcs = new TaskCompletionSource<string>(); + } + + [TearDown] + public void Cleanup() + { + channel.Dispose(); + server.ShutdownAsync().Wait(); + } + + [TestFixtureTearDown] + public void CleanupClass() + { + GrpcEnvironment.Shutdown(); + } + + [Test] + public void InfiniteDeadline() + { + // no deadline specified, check server sees infinite deadline + var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions()); + Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(callDetails, "RETURN_DEADLINE")); + + // DateTime.MaxValue deadline specified, check server sees infinite deadline + var callDetails2 = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions()); + Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(callDetails2, "RETURN_DEADLINE")); + } + + [Test] + public void DeadlineTransferredToServer() + { + var remainingTimeClient = TimeSpan.FromDays(7); + var deadline = DateTime.UtcNow + remainingTimeClient; + Thread.Sleep(1000); + var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions(deadline: deadline)); + + var serverDeadlineTicksString = Calls.BlockingUnaryCall(callDetails, "RETURN_DEADLINE"); + var serverDeadline = new DateTime(long.Parse(serverDeadlineTicksString), DateTimeKind.Utc); + + // A fairly relaxed check that the deadline set by client and deadline seen by server + // are in agreement. C core takes care of the work with transferring deadline over the wire, + // so we don't need an exact check here. + Assert.IsTrue(Math.Abs((deadline - serverDeadline).TotalMilliseconds) < 5000); + } + + [Test] + public void DeadlineInThePast() + { + var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions(deadline: DateTime.MinValue)); + + try + { + Calls.BlockingUnaryCall(callDetails, "TIMEOUT"); + Assert.Fail(); + } + catch (RpcException e) + { + // We can't guarantee the status code always DeadlineExceeded. See issue #2685. + Assert.Contains(e.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); + } + } + + [Test] + public void DeadlineExceededStatusOnTimeout() + { + var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)); + var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions(deadline: deadline)); + + try + { + Calls.BlockingUnaryCall(callDetails, "TIMEOUT"); + Assert.Fail(); + } + catch (RpcException e) + { + // We can't guarantee the status code always DeadlineExceeded. See issue #2685. + Assert.Contains(e.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); + } + } + + [Test] + public void ServerReceivesCancellationOnTimeout() + { + var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)); + var callDetails = new CallInvocationDetails<string, string>(channel, TestMethod, new CallOptions(deadline: deadline)); + + try + { + Calls.BlockingUnaryCall(callDetails, "CHECK_CANCELLATION_RECEIVED"); + Assert.Fail(); + } + catch (RpcException e) + { + // We can't guarantee the status code is always DeadlineExceeded. See issue #2685. + Assert.Contains(e.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); + } + Assert.AreEqual("CANCELLED", stringFromServerHandlerTcs.Task.Result); + } + + private static async Task<string> TestMethodHandler(string request, ServerCallContext context) + { + if (request == "TIMEOUT") + { + await Task.Delay(60000); + return ""; + } + + if (request == "RETURN_DEADLINE") + { + if (context.Deadline == DateTime.MaxValue) + { + return "DATETIME_MAXVALUE"; + } + + return context.Deadline.Ticks.ToString(); + } + + if (request == "CHECK_CANCELLATION_RECEIVED") + { + // wait until cancellation token is fired. + var tcs = new TaskCompletionSource<object>(); + context.CancellationToken.Register(() => { tcs.SetResult(null); }); + await tcs.Task; + stringFromServerHandlerTcs.SetResult("CANCELLED"); + return ""; + } + + return ""; + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/packages.config b/src/csharp/Grpc.Core.Tests/packages.config index 28af8d78c6..62077f41be 100644 --- a/src/csharp/Grpc.Core.Tests/packages.config +++ b/src/csharp/Grpc.Core.Tests/packages.config @@ -2,4 +2,5 @@ <packages> <package id="Ix-Async" version="1.2.3" targetFramework="net45" /> <package id="NUnit" version="2.6.4" targetFramework="net45" /> + <package id="NUnitTestAdapter" version="2.0.0" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/src/csharp/Grpc.Core/Call.cs b/src/csharp/Grpc.Core/CallInvocationDetails.cs index 37b452f020..eb23a3a209 100644 --- a/src/csharp/Grpc.Core/Call.cs +++ b/src/csharp/Grpc.Core/CallInvocationDetails.cs @@ -38,23 +38,30 @@ using Grpc.Core.Utils; namespace Grpc.Core { /// <summary> - /// Abstraction of a call to be invoked on a client. + /// Details about a client-side call to be invoked. /// </summary> - public class Call<TRequest, TResponse> + public class CallInvocationDetails<TRequest, TResponse> { - readonly string name; + readonly Channel channel; + readonly string method; + readonly string host; readonly Marshaller<TRequest> requestMarshaller; readonly Marshaller<TResponse> responseMarshaller; - readonly Channel channel; - readonly Metadata headers; + readonly CallOptions options; + + public CallInvocationDetails(Channel channel, Method<TRequest, TResponse> method, CallOptions options) : + this(channel, method.FullName, null, method.RequestMarshaller, method.ResponseMarshaller, options) + { + } - public Call(string serviceName, Method<TRequest, TResponse> method, Channel channel, Metadata headers) + public CallInvocationDetails(Channel channel, string method, string host, Marshaller<TRequest> requestMarshaller, Marshaller<TResponse> responseMarshaller, CallOptions options) { - this.name = method.GetFullName(serviceName); - this.requestMarshaller = method.RequestMarshaller; - this.responseMarshaller = method.ResponseMarshaller; this.channel = Preconditions.CheckNotNull(channel); - this.headers = Preconditions.CheckNotNull(headers); + this.method = Preconditions.CheckNotNull(method); + this.host = host; + this.requestMarshaller = Preconditions.CheckNotNull(requestMarshaller); + this.responseMarshaller = Preconditions.CheckNotNull(responseMarshaller); + this.options = Preconditions.CheckNotNull(options); } public Channel Channel @@ -65,25 +72,19 @@ namespace Grpc.Core } } - /// <summary> - /// Full methods name including the service name. - /// </summary> - public string Name + public string Method { get { - return name; + return this.method; } } - /// <summary> - /// Headers to send at the beginning of the call. - /// </summary> - public Metadata Headers + public string Host { get { - return headers; + return this.host; } } @@ -91,7 +92,7 @@ namespace Grpc.Core { get { - return requestMarshaller; + return this.requestMarshaller; } } @@ -99,7 +100,15 @@ namespace Grpc.Core { get { - return responseMarshaller; + return this.responseMarshaller; + } + } + + public CallOptions Options + { + get + { + return options; } } } diff --git a/src/csharp/Grpc.Core/CallOptions.cs b/src/csharp/Grpc.Core/CallOptions.cs new file mode 100644 index 0000000000..8e9739335f --- /dev/null +++ b/src/csharp/Grpc.Core/CallOptions.cs @@ -0,0 +1,89 @@ +#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.Threading; + +using Grpc.Core.Internal; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// <summary> + /// Options for calls made by client. + /// </summary> + public class CallOptions + { + readonly Metadata headers; + readonly DateTime deadline; + readonly CancellationToken cancellationToken; + + /// <summary> + /// Creates a new instance of <c>CallOptions</c>. + /// </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> + public CallOptions(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + // TODO(jtattermusch): consider only creating metadata object once it's really needed. + this.headers = headers != null ? headers : new Metadata(); + this.deadline = deadline.HasValue ? deadline.Value : DateTime.MaxValue; + this.cancellationToken = cancellationToken; + } + + /// <summary> + /// Headers to send at the beginning of the call. + /// </summary> + public Metadata Headers + { + get { return headers; } + } + + /// <summary> + /// Call deadline. + /// </summary> + public DateTime Deadline + { + get { return deadline; } + } + + /// <summary> + /// Token that can be used for cancelling the call. + /// </summary> + public CancellationToken CancellationToken + { + get { return cancellationToken; } + } + } +} diff --git a/src/csharp/Grpc.Core/Calls.cs b/src/csharp/Grpc.Core/Calls.cs index 359fe53741..00a8cabf82 100644 --- a/src/csharp/Grpc.Core/Calls.cs +++ b/src/csharp/Grpc.Core/Calls.cs @@ -43,70 +43,52 @@ namespace Grpc.Core /// </summary> public static class Calls { - public static TResponse BlockingUnaryCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, CancellationToken token) + public static TResponse BlockingUnaryCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call, TRequest req) where TRequest : class where TResponse : class { - var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); - // TODO(jtattermusch): this gives a race that cancellation can be requested before the call even starts. - RegisterCancellationCallback(asyncCall, token); - return asyncCall.UnaryCall(call.Channel, call.Name, req, call.Headers); + var asyncCall = new AsyncCall<TRequest, TResponse>(call); + return asyncCall.UnaryCall(req); } - public static AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, CancellationToken token) + public static AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call, TRequest req) where TRequest : class where TResponse : class { - var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); - asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name); - var asyncResult = asyncCall.UnaryCallAsync(req, call.Headers); - RegisterCancellationCallback(asyncCall, token); + var asyncCall = new AsyncCall<TRequest, TResponse>(call); + var asyncResult = asyncCall.UnaryCallAsync(req); return new AsyncUnaryCall<TResponse>(asyncResult, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); } - public static AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, CancellationToken token) + public static AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call, TRequest req) where TRequest : class where TResponse : class { - var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); - asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name); - asyncCall.StartServerStreamingCall(req, call.Headers); - RegisterCancellationCallback(asyncCall, token); + var asyncCall = new AsyncCall<TRequest, TResponse>(call); + asyncCall.StartServerStreamingCall(req); var responseStream = new ClientResponseStream<TRequest, TResponse>(asyncCall); return new AsyncServerStreamingCall<TResponse>(responseStream, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); } - public static AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, CancellationToken token) + public static AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call) where TRequest : class where TResponse : class { - var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); - asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name); - var resultTask = asyncCall.ClientStreamingCallAsync(call.Headers); - RegisterCancellationCallback(asyncCall, token); + var asyncCall = new AsyncCall<TRequest, TResponse>(call); + var resultTask = asyncCall.ClientStreamingCallAsync(); var requestStream = new ClientRequestStream<TRequest, TResponse>(asyncCall); return new AsyncClientStreamingCall<TRequest, TResponse>(requestStream, resultTask, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); } - public static AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, CancellationToken token) + public static AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(CallInvocationDetails<TRequest, TResponse> call) where TRequest : class where TResponse : class { - var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); - asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name); - asyncCall.StartDuplexStreamingCall(call.Headers); - RegisterCancellationCallback(asyncCall, token); + var asyncCall = new AsyncCall<TRequest, TResponse>(call); + asyncCall.StartDuplexStreamingCall(); var requestStream = new ClientRequestStream<TRequest, TResponse>(asyncCall); var responseStream = new ClientResponseStream<TRequest, TResponse>(asyncCall); return new AsyncDuplexStreamingCall<TRequest, TResponse>(requestStream, responseStream, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); } - - private static void RegisterCancellationCallback<TRequest, TResponse>(AsyncCall<TRequest, TResponse> asyncCall, CancellationToken token) - { - if (token.CanBeCanceled) - { - token.Register(() => asyncCall.Cancel()); - } - } } } diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs index e5c6abd2cb..9273ea4582 100644 --- a/src/csharp/Grpc.Core/Channel.cs +++ b/src/csharp/Grpc.Core/Channel.cs @@ -37,6 +37,8 @@ using System.Threading; using System.Threading.Tasks; using Grpc.Core.Internal; +using Grpc.Core.Logging; +using Grpc.Core.Utils; namespace Grpc.Core { @@ -45,89 +47,134 @@ namespace Grpc.Core /// </summary> public class Channel : IDisposable { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<Channel>(); + readonly GrpcEnvironment environment; readonly ChannelSafeHandle handle; readonly List<ChannelOption> options; - readonly string target; bool disposed; /// <summary> /// Creates a channel that connects to a specific host. - /// Port will default to 80 for an unsecure channel and to 443 a secure channel. + /// Port will default to 80 for an unsecure channel and to 443 for a secure channel. /// </summary> - /// <param name="host">The DNS name of IP address of the host.</param> - /// <param name="credentials">Optional credentials to create a secure channel.</param> + /// <param name="host">The name or IP address of the host.</param> + /// <param name="credentials">Credentials to secure the channel.</param> /// <param name="options">Channel options.</param> - public Channel(string host, Credentials credentials = null, IEnumerable<ChannelOption> options = null) + public Channel(string host, Credentials credentials, IEnumerable<ChannelOption> options = null) { + Preconditions.CheckNotNull(host); this.environment = GrpcEnvironment.GetInstance(); this.options = options != null ? new List<ChannelOption>(options) : new List<ChannelOption>(); EnsureUserAgentChannelOption(this.options); + using (CredentialsSafeHandle nativeCredentials = credentials.ToNativeCredentials()) using (ChannelArgsSafeHandle nativeChannelArgs = ChannelOptions.CreateChannelArgs(this.options)) { - if (credentials != null) + if (nativeCredentials != null) { - using (CredentialsSafeHandle nativeCredentials = credentials.ToNativeCredentials()) - { - this.handle = ChannelSafeHandle.CreateSecure(nativeCredentials, host, nativeChannelArgs); - } + this.handle = ChannelSafeHandle.CreateSecure(nativeCredentials, host, nativeChannelArgs); } else { - this.handle = ChannelSafeHandle.Create(host, nativeChannelArgs); + this.handle = ChannelSafeHandle.CreateInsecure(host, nativeChannelArgs); } } - this.target = GetOverridenTarget(host, this.options); } /// <summary> /// Creates a channel that connects to a specific host and port. /// </summary> - /// <param name="host">DNS name or IP address</param> - /// <param name="port">the port</param> - /// <param name="credentials">Optional credentials to create a secure channel.</param> + /// <param name="host">The name or IP address of the host.</param> + /// <param name="port">The port.</param> + /// <param name="credentials">Credentials to secure the channel.</param> /// <param name="options">Channel options.</param> - public Channel(string host, int port, Credentials credentials = null, IEnumerable<ChannelOption> options = null) : + public Channel(string host, int port, Credentials credentials, IEnumerable<ChannelOption> options = null) : this(string.Format("{0}:{1}", host, port), credentials, options) { } - public void Dispose() + /// <summary> + /// Gets current connectivity state of this channel. + /// </summary> + public ChannelState State { - Dispose(true); - GC.SuppressFinalize(this); + get + { + return handle.CheckConnectivityState(false); + } } - internal string Target + /// <summary> + /// Returned tasks completes once channel state has become different from + /// given lastObservedState. + /// If deadline is reached or and error occurs, returned task is cancelled. + /// </summary> + public Task WaitForStateChangedAsync(ChannelState lastObservedState, DateTime? deadline = null) { - get + Preconditions.CheckArgument(lastObservedState != ChannelState.FatalFailure, + "FatalFailure is a terminal state. No further state changes can occur."); + var tcs = new TaskCompletionSource<object>(); + var deadlineTimespec = deadline.HasValue ? Timespec.FromDateTime(deadline.Value) : Timespec.InfFuture; + var handler = new BatchCompletionDelegate((success, ctx) => { - return target; - } + if (success) + { + tcs.SetResult(null); + } + else + { + tcs.SetCanceled(); + } + }); + handle.WatchConnectivityState(lastObservedState, deadlineTimespec, environment.CompletionQueue, environment.CompletionRegistry, handler); + return tcs.Task; } - internal ChannelSafeHandle Handle + /// <summary> Address of the remote endpoint in URI format.</summary> + public string Target { get { - return this.handle; + return handle.GetTarget(); } } - internal CompletionQueueSafeHandle CompletionQueue + /// <summary> + /// Allows explicitly requesting channel to connect without starting an RPC. + /// Returned task completes once state Ready was seen. If the deadline is reached, + /// or channel enters the FatalFailure state, the task is cancelled. + /// There is no need to call this explicitly unless your use case requires that. + /// Starting an RPC on a new channel will request connection implicitly. + /// </summary> + public async Task ConnectAsync(DateTime? deadline = null) { - get + var currentState = handle.CheckConnectivityState(true); + while (currentState != ChannelState.Ready) { - return this.environment.CompletionQueue; + if (currentState == ChannelState.FatalFailure) + { + throw new OperationCanceledException("Channel has reached FatalFailure state."); + } + await WaitForStateChangedAsync(currentState, deadline); + currentState = handle.CheckConnectivityState(false); } } - internal CompletionRegistry CompletionRegistry + /// <summary> + /// Destroys the underlying channel. + /// </summary> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + internal ChannelSafeHandle Handle { get { - return this.environment.CompletionRegistry; + return this.handle; } } @@ -161,26 +208,5 @@ namespace Grpc.Core // TODO(jtattermusch): it would be useful to also provide .NET/mono version. return string.Format("grpc-csharp/{0}", VersionInfo.CurrentVersion); } - - /// <summary> - /// Look for SslTargetNameOverride option and return its value instead of originalTarget - /// if found. - /// </summary> - private static string GetOverridenTarget(string originalTarget, IEnumerable<ChannelOption> options) - { - if (options == null) - { - return originalTarget; - } - foreach (var option in options) - { - if (option.Type == ChannelOption.OptionType.String - && option.Name == ChannelOptions.SslTargetNameOverride) - { - return option.StringValue; - } - } - return originalTarget; - } } } diff --git a/src/csharp/Grpc.Core/ChannelOptions.cs b/src/csharp/Grpc.Core/ChannelOptions.cs index 9fe03d2805..1e0f90287a 100644 --- a/src/csharp/Grpc.Core/ChannelOptions.cs +++ b/src/csharp/Grpc.Core/ChannelOptions.cs @@ -30,7 +30,6 @@ #endregion using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -135,6 +134,9 @@ namespace Grpc.Core /// <summary>Initial sequence number for http2 transports</summary> public const string Http2InitialSequenceNumber = "grpc.http2.initial_sequence_number"; + /// <summary>Default authority for calls.</summary> + public const string DefaultAuthority = "grpc.default_authority"; + /// <summary>Primary user agent: goes at the start of the user-agent metadata</summary> public const string PrimaryUserAgentString = "grpc.primary_user_agent"; diff --git a/src/csharp/Grpc.Core/ChannelState.cs b/src/csharp/Grpc.Core/ChannelState.cs new file mode 100644 index 0000000000..d293b98f75 --- /dev/null +++ b/src/csharp/Grpc.Core/ChannelState.cs @@ -0,0 +1,69 @@ +#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 +{ + /// <summary> + /// Connectivity state of a channel. + /// Based on grpc_connectivity_state from grpc/grpc.h + /// </summary> + public enum ChannelState + { + /// <summary> + /// Channel is idle + /// </summary> + Idle, + + /// <summary> + /// Channel is connecting + /// </summary> + Connecting, + + /// <summary> + /// Channel is ready for work + /// </summary> + Ready, + + /// <summary> + /// Channel has seen a failure but expects to recover + /// </summary> + TransientFailure, + + /// <summary> + /// Channel has seen a failure that it cannot recover from + /// </summary> + FatalFailure + } +} diff --git a/src/csharp/Grpc.Core/ClientBase.cs b/src/csharp/Grpc.Core/ClientBase.cs index a099f96aea..88494bb4ac 100644 --- a/src/csharp/Grpc.Core/ClientBase.cs +++ b/src/csharp/Grpc.Core/ClientBase.cs @@ -76,19 +76,17 @@ namespace Grpc.Core /// <summary> /// Creates a new call to given method. /// </summary> - protected Call<TRequest, TResponse> CreateCall<TRequest, TResponse>(string serviceName, Method<TRequest, TResponse> method, Metadata metadata) + protected CallInvocationDetails<TRequest, TResponse> CreateCall<TRequest, TResponse>(Method<TRequest, TResponse> method, CallOptions options) where TRequest : class where TResponse : class { var interceptor = HeaderInterceptor; if (interceptor != null) { - metadata = metadata ?? new Metadata(); - interceptor(metadata); - metadata.Freeze(); + interceptor(options.Headers); + options.Headers.Freeze(); } - metadata = metadata ?? Metadata.Empty; - return new Call<TRequest, TResponse>(serviceName, method, channel, metadata); + return new CallInvocationDetails<TRequest, TResponse>(channel, method, options); } } } diff --git a/src/csharp/Grpc.Core/Credentials.cs b/src/csharp/Grpc.Core/Credentials.cs index e64c1e3dc1..4fcac0c4c0 100644 --- a/src/csharp/Grpc.Core/Credentials.cs +++ b/src/csharp/Grpc.Core/Credentials.cs @@ -41,39 +41,98 @@ namespace Grpc.Core /// </summary> public abstract class Credentials { + static readonly Credentials InsecureInstance = new InsecureCredentialsImpl(); + + /// <summary> + /// Returns instance of credential that provides no security and + /// will result in creating an unsecure channel with no encryption whatsoever. + /// </summary> + public static Credentials Insecure + { + get + { + return InsecureInstance; + } + } + /// <summary> - /// Creates native object for the credentials. + /// Creates native object for the credentials. May return null if insecure channel + /// should be created. /// </summary> /// <returns>The native credentials.</returns> internal abstract CredentialsSafeHandle ToNativeCredentials(); + + private sealed class InsecureCredentialsImpl : Credentials + { + internal override CredentialsSafeHandle ToNativeCredentials() + { + return null; + } + } } /// <summary> /// Client-side SSL credentials. /// </summary> - public class SslCredentials : Credentials + public sealed class SslCredentials : Credentials { - string pemRootCerts; + readonly string rootCertificates; + readonly KeyCertificatePair keyCertificatePair; - public SslCredentials(string pemRootCerts) + /// <summary> + /// Creates client-side SSL credentials loaded from + /// disk file pointed to by the GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable. + /// If that fails, gets the roots certificates from a well known place on disk. + /// </summary> + public SslCredentials() : this(null, null) { - this.pemRootCerts = pemRootCerts; + } + + /// <summary> + /// Creates client-side SSL credentials from + /// a string containing PEM encoded root certificates. + /// </summary> + public SslCredentials(string rootCertificates) : this(rootCertificates, null) + { + } + + /// <summary> + /// Creates client-side SSL credentials. + /// </summary> + /// <param name="rootCertificates">string containing PEM encoded server root certificates.</param> + /// <param name="keyCertificatePair">a key certificate pair.</param> + public SslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair) + { + this.rootCertificates = rootCertificates; + this.keyCertificatePair = keyCertificatePair; } /// <summary> /// PEM encoding of the server root certificates. /// </summary> - public string RootCerts + public string RootCertificates + { + get + { + return this.rootCertificates; + } + } + + /// <summary> + /// Client side key and certificate pair. + /// If null, client will not use key and certificate pair. + /// </summary> + public KeyCertificatePair KeyCertificatePair { get { - return this.pemRootCerts; + return this.keyCertificatePair; } } internal override CredentialsSafeHandle ToNativeCredentials() { - return CredentialsSafeHandle.CreateSslCredentials(pemRootCerts); + return CredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair); } } } diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index fd68b91851..52defd1965 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -21,24 +21,29 @@ <DefineConstants>DEBUG;</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <ConsolePause>false</ConsolePause> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> - <DebugType>full</DebugType> + <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <ConsolePause>false</ConsolePause> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\ReleaseSigned</OutputPath> + <DefineConstants>SIGNED</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> </PropertyGroup> <ItemGroup> <Reference Include="System" /> <Reference Include="System.Interactive.Async"> <HintPath>..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll</HintPath> </Reference> - <Reference Include="System.Collections.Immutable"> - <HintPath>..\packages\System.Collections.Immutable.1.1.36\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath> - </Reference> </ItemGroup> <ItemGroup> <Compile Include="AsyncDuplexStreamingCall.cs" /> @@ -47,12 +52,11 @@ <Compile Include="IServerStreamWriter.cs" /> <Compile Include="IAsyncStreamWriter.cs" /> <Compile Include="IAsyncStreamReader.cs" /> - <Compile Include="Internal\GrpcLog.cs" /> + <Compile Include="ServerPort.cs" /> <Compile Include="Version.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="RpcException.cs" /> <Compile Include="Calls.cs" /> - <Compile Include="Call.cs" /> <Compile Include="AsyncClientStreamingCall.cs" /> <Compile Include="GrpcEnvironment.cs" /> <Compile Include="Status.cs" /> @@ -103,6 +107,14 @@ <Compile Include="ChannelOptions.cs" /> <Compile Include="AsyncUnaryCall.cs" /> <Compile Include="VersionInfo.cs" /> + <Compile Include="Internal\CStringSafeHandle.cs" /> + <Compile Include="KeyCertificatePair.cs" /> + <Compile Include="Logging\ILogger.cs" /> + <Compile Include="Logging\ConsoleLogger.cs" /> + <Compile Include="Internal\NativeLogRedirector.cs" /> + <Compile Include="ChannelState.cs" /> + <Compile Include="CallInvocationDetails.cs" /> + <Compile Include="CallOptions.cs" /> </ItemGroup> <ItemGroup> <None Include="Grpc.Core.nuspec" /> @@ -133,4 +145,5 @@ </Target> <Import Project="..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets" Condition="Exists('..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets')" /> <Import Project="..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets" Condition="Exists('..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets')" /> + <ItemGroup /> </Project>
\ No newline at end of file diff --git a/src/csharp/Grpc.Core/Grpc.Core.nuspec b/src/csharp/Grpc.Core/Grpc.Core.nuspec index 5ace6dcf89..fe49efc7ec 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.nuspec +++ b/src/csharp/Grpc.Core/Grpc.Core.nuspec @@ -15,15 +15,14 @@ <copyright>Copyright 2015, Google Inc.</copyright> <tags>gRPC RPC Protocol HTTP/2</tags> <dependencies> - <dependency id="System.Collections.Immutable" version="1.1.36" /> <dependency id="Ix-Async" version="1.2.3" /> <dependency id="grpc.native.csharp_ext" version="$GrpcNativeCsharpExtVersion$" /> </dependencies> </metadata> <files> - <file src="bin/Release/Grpc.Core.dll" target="lib/net45" /> - <file src="bin/Release/Grpc.Core.pdb" target="lib/net45" /> - <file src="bin/Release/Grpc.Core.xml" target="lib/net45" /> + <file src="bin/ReleaseSigned/Grpc.Core.dll" target="lib/net45" /> + <file src="bin/ReleaseSigned/Grpc.Core.pdb" target="lib/net45" /> + <file src="bin/ReleaseSigned/Grpc.Core.xml" target="lib/net45" /> <file src="**\*.cs" target="src" /> </files> </package> diff --git a/src/csharp/Grpc.Core/GrpcEnvironment.cs b/src/csharp/Grpc.Core/GrpcEnvironment.cs index 47d1651aab..034a66be3c 100644 --- a/src/csharp/Grpc.Core/GrpcEnvironment.cs +++ b/src/csharp/Grpc.Core/GrpcEnvironment.cs @@ -35,6 +35,7 @@ using System; using System.Runtime.InteropServices; using System.Threading.Tasks; using Grpc.Core.Internal; +using Grpc.Core.Logging; using Grpc.Core.Utils; namespace Grpc.Core @@ -55,6 +56,8 @@ namespace Grpc.Core static object staticLock = new object(); static GrpcEnvironment instance; + static ILogger logger = new ConsoleLogger(); + readonly GrpcThreadPool threadPool; readonly CompletionRegistry completionRegistry; readonly DebugStats debugStats = new DebugStats(); @@ -93,17 +96,38 @@ namespace Grpc.Core } /// <summary> + /// Gets application-wide logger used by gRPC. + /// </summary> + /// <value>The logger.</value> + public static ILogger Logger + { + get + { + return logger; + } + } + + /// <summary> + /// Sets the application-wide logger that should be used by gRPC. + /// </summary> + public static void SetLogger(ILogger customLogger) + { + Preconditions.CheckNotNull(customLogger); + logger = customLogger; + } + + /// <summary> /// Creates gRPC environment. /// </summary> private GrpcEnvironment() { - GrpcLog.RedirectNativeLogs(Console.Error); + NativeLogRedirector.Redirect(); grpcsharp_init(); completionRegistry = new CompletionRegistry(this); threadPool = new GrpcThreadPool(this, THREAD_POOL_SIZE); threadPool.Start(); // TODO: use proper logging here - Console.WriteLine("GRPC initialized."); + Logger.Info("gRPC initialized."); } /// <summary> @@ -154,8 +178,7 @@ namespace Grpc.Core debugStats.CheckOK(); - // TODO: use proper logging here - Console.WriteLine("GRPC shutdown."); + Logger.Info("gRPC shutdown."); } /// <summary> @@ -171,7 +194,7 @@ namespace Grpc.Core } catch (Exception e) { - Console.WriteLine("Error occured while shutting down GrpcEnvironment: " + 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 f983dbb759..414b5c4282 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs @@ -38,6 +38,7 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Grpc.Core.Internal; +using Grpc.Core.Logging; using Grpc.Core.Utils; namespace Grpc.Core.Internal @@ -47,7 +48,9 @@ namespace Grpc.Core.Internal /// </summary> internal class AsyncCall<TRequest, TResponse> : AsyncCallBase<TRequest, TResponse> { - Channel channel; + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<AsyncCall<TRequest, TResponse>>(); + + readonly CallInvocationDetails<TRequest, TResponse> callDetails; // Completion of a pending unary response if not null. TaskCompletionSource<TResponse> unaryResponseTcs; @@ -57,26 +60,18 @@ namespace Grpc.Core.Internal bool readObserverCompleted; // True if readObserver has already been completed. - public AsyncCall(Func<TRequest, byte[]> serializer, Func<byte[], TResponse> deserializer) : base(serializer, deserializer) + public AsyncCall(CallInvocationDetails<TRequest, TResponse> callDetails) + : base(callDetails.RequestMarshaller.Serializer, callDetails.ResponseMarshaller.Deserializer) { - } - - public void Initialize(Channel channel, CompletionQueueSafeHandle cq, string methodName) - { - this.channel = channel; - var call = CallSafeHandle.Create(channel.Handle, channel.CompletionRegistry, cq, methodName, channel.Target, Timespec.InfFuture); - channel.Environment.DebugStats.ActiveClientCalls.Increment(); - InitializeInternal(call); + this.callDetails = callDetails; } // TODO: this method is not Async, so it shouldn't be in AsyncCall class, but // it is reusing fair amount of code in this class, so we are leaving it here. - // TODO: for other calls, you need to call Initialize, this methods calls initialize - // on its own, so there's a usage inconsistency. /// <summary> /// Blocking unary request - unary response call. /// </summary> - public TResponse UnaryCall(Channel channel, string methodName, TRequest msg, Metadata headers) + public TResponse UnaryCall(TRequest msg) { using (CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.Create()) { @@ -86,13 +81,15 @@ namespace Grpc.Core.Internal lock (myLock) { - Initialize(channel, cq, methodName); + Preconditions.CheckState(!started); started = true; + Initialize(cq); + halfcloseRequested = true; readingDone = true; } - using (var metadataArray = MetadataArraySafeHandle.Create(headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers)) { using (var ctx = BatchContextSafeHandle.Create()) { @@ -106,7 +103,7 @@ namespace Grpc.Core.Internal } catch (Exception e) { - Console.WriteLine("Exception occured while invoking completion delegate: " + e); + Logger.Error(e, "Exception occured while invoking completion delegate."); } } } @@ -126,20 +123,22 @@ namespace Grpc.Core.Internal /// <summary> /// Starts a unary request - unary response call. /// </summary> - public Task<TResponse> UnaryCallAsync(TRequest msg, Metadata headers) + public Task<TResponse> UnaryCallAsync(TRequest msg) { lock (myLock) { - Preconditions.CheckNotNull(call); - + Preconditions.CheckState(!started); started = true; + + Initialize(callDetails.Channel.Environment.CompletionQueue); + halfcloseRequested = true; readingDone = true; byte[] payload = UnsafeSerialize(msg); unaryResponseTcs = new TaskCompletionSource<TResponse>(); - using (var metadataArray = MetadataArraySafeHandle.Create(headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers)) { call.StartUnary(payload, HandleUnaryResponse, metadataArray); } @@ -151,17 +150,19 @@ namespace Grpc.Core.Internal /// Starts a streamed request - unary response call. /// Use StartSendMessage and StartSendCloseFromClient to stream requests. /// </summary> - public Task<TResponse> ClientStreamingCallAsync(Metadata headers) + public Task<TResponse> ClientStreamingCallAsync() { lock (myLock) { - Preconditions.CheckNotNull(call); - + Preconditions.CheckState(!started); started = true; + + Initialize(callDetails.Channel.Environment.CompletionQueue); + readingDone = true; unaryResponseTcs = new TaskCompletionSource<TResponse>(); - using (var metadataArray = MetadataArraySafeHandle.Create(headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers)) { call.StartClientStreaming(HandleUnaryResponse, metadataArray); } @@ -173,19 +174,21 @@ namespace Grpc.Core.Internal /// <summary> /// Starts a unary request - streamed response call. /// </summary> - public void StartServerStreamingCall(TRequest msg, Metadata headers) + public void StartServerStreamingCall(TRequest msg) { lock (myLock) { - Preconditions.CheckNotNull(call); - + Preconditions.CheckState(!started); started = true; + + Initialize(callDetails.Channel.Environment.CompletionQueue); + halfcloseRequested = true; halfclosed = true; // halfclose not confirmed yet, but it will be once finishedHandler is called. byte[] payload = UnsafeSerialize(msg); - using (var metadataArray = MetadataArraySafeHandle.Create(headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers)) { call.StartServerStreaming(payload, HandleFinished, metadataArray); } @@ -196,15 +199,16 @@ namespace Grpc.Core.Internal /// Starts a streaming request - streaming response call. /// Use StartSendMessage and StartSendCloseFromClient to stream requests. /// </summary> - public void StartDuplexStreamingCall(Metadata headers) + public void StartDuplexStreamingCall() { lock (myLock) { - Preconditions.CheckNotNull(call); - + Preconditions.CheckState(!started); started = true; - using (var metadataArray = MetadataArraySafeHandle.Create(headers)) + Initialize(callDetails.Channel.Environment.CompletionQueue); + + using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers)) { call.StartDuplexStreaming(HandleFinished, metadataArray); } @@ -306,7 +310,26 @@ namespace Grpc.Core.Internal protected override void OnReleaseResources() { - channel.Environment.DebugStats.ActiveClientCalls.Decrement(); + callDetails.Channel.Environment.DebugStats.ActiveClientCalls.Decrement(); + } + + private void Initialize(CompletionQueueSafeHandle cq) + { + var call = callDetails.Channel.Handle.CreateCall(callDetails.Channel.Environment.CompletionRegistry, cq, + callDetails.Method, callDetails.Host, Timespec.FromDateTime(callDetails.Options.Deadline)); + callDetails.Channel.Environment.DebugStats.ActiveClientCalls.Increment(); + InitializeInternal(call); + RegisterCancellationCallback(); + } + + // Make sure that once cancellationToken for this call is cancelled, Cancel() will be called. + private void RegisterCancellationCallback() + { + var token = callDetails.Options.CancellationToken; + if (token.CanBeCanceled) + { + token.Register(() => this.Cancel()); + } } /// <summary> diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs index 64713c8c52..38f2a5baeb 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs @@ -38,6 +38,7 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Grpc.Core.Internal; +using Grpc.Core.Logging; using Grpc.Core.Utils; namespace Grpc.Core.Internal @@ -48,6 +49,8 @@ namespace Grpc.Core.Internal /// </summary> internal abstract class AsyncCallBase<TWrite, TRead> { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<AsyncCallBase<TWrite, TRead>>(); + readonly Func<TWrite, byte[]> serializer; readonly Func<byte[], TRead> deserializer; @@ -233,9 +236,9 @@ namespace Grpc.Core.Internal payload = serializer(msg); return true; } - catch (Exception) + catch (Exception e) { - Console.WriteLine("Exception occured while trying to serialize message"); + Logger.Error(e, "Exception occured while trying to serialize message"); payload = null; return false; } @@ -248,9 +251,9 @@ namespace Grpc.Core.Internal msg = deserializer(payload); return true; } - catch (Exception) + catch (Exception e) { - Console.WriteLine("Exception occured while trying to deserialize message"); + Logger.Error(e, "Exception occured while trying to deserialize message."); msg = default(TRead); return false; } @@ -264,7 +267,7 @@ namespace Grpc.Core.Internal } catch (Exception e) { - Console.WriteLine("Exception occured while invoking completion delegate: " + e); + Logger.Error(e, "Exception occured while invoking completion delegate."); } } diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs index f809f4a84c..513902ee36 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs @@ -48,6 +48,7 @@ namespace Grpc.Core.Internal internal class AsyncCallServer<TRequest, TResponse> : AsyncCallBase<TResponse, TRequest> { readonly TaskCompletionSource<object> finishedServersideTcs = new TaskCompletionSource<object>(); + readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); readonly GrpcEnvironment environment; public AsyncCallServer(Func<TResponse, byte[]> serializer, Func<byte[], TRequest> deserializer, GrpcEnvironment environment) : base(serializer, deserializer) @@ -118,6 +119,26 @@ namespace Grpc.Core.Internal } } + /// <summary> + /// Gets cancellation token that gets cancelled once close completion + /// is received and the cancelled flag is set. + /// </summary> + public CancellationToken CancellationToken + { + get + { + return cancellationTokenSource.Token; + } + } + + public string Peer + { + get + { + return call.GetPeer(); + } + } + protected override void OnReleaseResources() { environment.DebugStats.ActiveServerCalls.Decrement(); @@ -138,6 +159,8 @@ namespace Grpc.Core.Internal { // Once we cancel, we don't have to care that much // about reads and writes. + + // TODO(jtattermusch): is this still necessary? Cancel(); } @@ -145,6 +168,11 @@ namespace Grpc.Core.Internal } // TODO(jtattermusch): handle error + if (cancelled) + { + cancellationTokenSource.Cancel(); + } + finishedServersideTcs.SetResult(null); } } diff --git a/src/csharp/Grpc.Core/Internal/CStringSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CStringSafeHandle.cs new file mode 100644 index 0000000000..92fbe8cf0f --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/CStringSafeHandle.cs @@ -0,0 +1,60 @@ +#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.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace Grpc.Core.Internal +{ + /// <summary> + /// Owned char* object. + /// </summary> + internal class CStringSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("grpc_csharp_ext.dll")] + static extern void gprsharp_free(IntPtr ptr); + + private CStringSafeHandle() + { + } + + public string GetValue() + { + return Marshal.PtrToStringAnsi(handle); + } + + protected override bool ReleaseHandle() + { + gprsharp_free(handle); + return true; + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs index 19dbb83f24..714749b171 100644 --- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs @@ -46,9 +46,6 @@ namespace Grpc.Core.Internal CompletionRegistry completionRegistry; [DllImport("grpc_csharp_ext.dll")] - static extern CallSafeHandle grpcsharp_channel_create_call(ChannelSafeHandle channel, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline); - - [DllImport("grpc_csharp_ext.dll")] static extern GRPCCallError grpcsharp_call_cancel(CallSafeHandle call); [DllImport("grpc_csharp_ext.dll")] @@ -92,19 +89,15 @@ namespace Grpc.Core.Internal BatchContextSafeHandle ctx); [DllImport("grpc_csharp_ext.dll")] + static extern CStringSafeHandle grpcsharp_call_get_peer(CallSafeHandle call); + + [DllImport("grpc_csharp_ext.dll")] static extern void grpcsharp_call_destroy(IntPtr call); private CallSafeHandle() { } - public static CallSafeHandle Create(ChannelSafeHandle channel, CompletionRegistry registry, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline) - { - var result = grpcsharp_channel_create_call(channel, cq, method, host, deadline); - result.SetCompletionRegistry(registry); - return result; - } - public void SetCompletionRegistry(CompletionRegistry completionRegistry) { this.completionRegistry = completionRegistry; @@ -190,6 +183,14 @@ namespace Grpc.Core.Internal grpcsharp_call_cancel_with_status(this, status.StatusCode, status.Detail).CheckOk(); } + public string GetPeer() + { + using (var cstring = grpcsharp_call_get_peer(this)) + { + return cstring.GetValue(); + } + } + protected override bool ReleaseHandle() { grpcsharp_call_destroy(handle); diff --git a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs index f046f4c6d0..7324ebdf57 100644 --- a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs @@ -41,21 +41,34 @@ namespace Grpc.Core.Internal internal class ChannelSafeHandle : SafeHandleZeroIsInvalid { [DllImport("grpc_csharp_ext.dll")] - static extern ChannelSafeHandle grpcsharp_channel_create(string target, ChannelArgsSafeHandle channelArgs); + static extern ChannelSafeHandle grpcsharp_insecure_channel_create(string target, ChannelArgsSafeHandle channelArgs); [DllImport("grpc_csharp_ext.dll")] static extern ChannelSafeHandle grpcsharp_secure_channel_create(CredentialsSafeHandle credentials, string target, ChannelArgsSafeHandle channelArgs); [DllImport("grpc_csharp_ext.dll")] + static extern CallSafeHandle grpcsharp_channel_create_call(ChannelSafeHandle channel, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline); + + [DllImport("grpc_csharp_ext.dll")] + static extern ChannelState grpcsharp_channel_check_connectivity_state(ChannelSafeHandle channel, int tryToConnect); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_channel_watch_connectivity_state(ChannelSafeHandle channel, ChannelState lastObservedState, + Timespec deadline, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx); + + [DllImport("grpc_csharp_ext.dll")] + static extern CStringSafeHandle grpcsharp_channel_get_target(ChannelSafeHandle call); + + [DllImport("grpc_csharp_ext.dll")] static extern void grpcsharp_channel_destroy(IntPtr channel); private ChannelSafeHandle() { } - public static ChannelSafeHandle Create(string target, ChannelArgsSafeHandle channelArgs) + public static ChannelSafeHandle CreateInsecure(string target, ChannelArgsSafeHandle channelArgs) { - return grpcsharp_channel_create(target, channelArgs); + return grpcsharp_insecure_channel_create(target, channelArgs); } public static ChannelSafeHandle CreateSecure(CredentialsSafeHandle credentials, string target, ChannelArgsSafeHandle channelArgs) @@ -63,6 +76,34 @@ namespace Grpc.Core.Internal return grpcsharp_secure_channel_create(credentials, target, channelArgs); } + public CallSafeHandle CreateCall(CompletionRegistry registry, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline) + { + var result = grpcsharp_channel_create_call(this, cq, method, host, deadline); + result.SetCompletionRegistry(registry); + return result; + } + + public ChannelState CheckConnectivityState(bool tryToConnect) + { + return grpcsharp_channel_check_connectivity_state(this, tryToConnect ? 1 : 0); + } + + public void WatchConnectivityState(ChannelState lastObservedState, Timespec deadline, CompletionQueueSafeHandle cq, + CompletionRegistry completionRegistry, BatchCompletionDelegate callback) + { + var ctx = BatchContextSafeHandle.Create(); + completionRegistry.RegisterBatchCompletion(ctx, callback); + grpcsharp_channel_watch_connectivity_state(this, lastObservedState, deadline, cq, ctx); + } + + public string GetTarget() + { + using (var cstring = grpcsharp_channel_get_target(this)) + { + return cstring.GetValue(); + } + } + protected override bool ReleaseHandle() { grpcsharp_channel_destroy(handle); diff --git a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs index f6d8aa0600..2796c959a3 100644 --- a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs +++ b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs @@ -35,6 +35,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Runtime.InteropServices; +using Grpc.Core.Logging; using Grpc.Core.Utils; namespace Grpc.Core.Internal @@ -45,6 +46,8 @@ namespace Grpc.Core.Internal internal class CompletionRegistry { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<CompletionRegistry>(); + readonly GrpcEnvironment environment; readonly ConcurrentDictionary<IntPtr, OpCompletionDelegate> dict = new ConcurrentDictionary<IntPtr, OpCompletionDelegate>(); @@ -81,7 +84,7 @@ namespace Grpc.Core.Internal } catch (Exception e) { - Console.WriteLine("Exception occured while invoking completion delegate: " + e); + Logger.Error(e, "Exception occured while invoking completion delegate."); } finally { diff --git a/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs index f361199068..8b4fa85e5d 100644 --- a/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs @@ -50,9 +50,23 @@ namespace Grpc.Core.Internal { } - public static CredentialsSafeHandle CreateSslCredentials(string pemRootCerts) + public static CredentialsSafeHandle CreateNullCredentials() { - return grpcsharp_ssl_credentials_create(pemRootCerts, null, null); + var creds = new CredentialsSafeHandle(); + creds.SetHandle(IntPtr.Zero); + return creds; + } + + public static CredentialsSafeHandle CreateSslCredentials(string pemRootCerts, KeyCertificatePair keyCertPair) + { + if (keyCertPair != null) + { + return grpcsharp_ssl_credentials_create(pemRootCerts, keyCertPair.CertificateChain, keyCertPair.PrivateKey); + } + else + { + return grpcsharp_ssl_credentials_create(pemRootCerts, null, null); + } } protected override bool ReleaseHandle() diff --git a/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs index b77e893044..cb4c7c821e 100644 --- a/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs +++ b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs @@ -36,7 +36,7 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using Grpc.Core.Internal; +using Grpc.Core.Logging; namespace Grpc.Core.Internal { @@ -45,6 +45,8 @@ namespace Grpc.Core.Internal /// </summary> internal class GrpcThreadPool { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<GrpcThreadPool>(); + readonly GrpcEnvironment environment; readonly object myLock = new object(); readonly List<Thread> threads = new List<Thread>(); @@ -82,7 +84,7 @@ namespace Grpc.Core.Internal { cq.Shutdown(); - Console.WriteLine("Waiting for GRPC threads to finish."); + Logger.Info("Waiting for GRPC threads to finish."); foreach (var thread in threads) { thread.Join(); @@ -129,12 +131,12 @@ namespace Grpc.Core.Internal } catch (Exception e) { - Console.WriteLine("Exception occured while invoking completion delegate: " + e); + Logger.Error(e, "Exception occured while invoking completion delegate"); } } } while (ev.type != GRPCCompletionType.Shutdown); - Console.WriteLine("Completion queue has shutdown successfully, thread " + Thread.CurrentThread.Name + " exiting."); + Logger.Info("Completion queue has shutdown successfully, thread {0} exiting.", Thread.CurrentThread.Name); } } } diff --git a/src/csharp/Grpc.Core/Internal/GrpcLog.cs b/src/csharp/Grpc.Core/Internal/NativeLogRedirector.cs index 2f3c8ad71c..b8a55c5fe8 100644 --- a/src/csharp/Grpc.Core/Internal/GrpcLog.cs +++ b/src/csharp/Grpc.Core/Internal/NativeLogRedirector.cs @@ -44,30 +44,26 @@ namespace Grpc.Core.Internal /// <summary> /// Logs from gRPC C core library can get lost if your application is not a console app. - /// This class allows redirection of logs to arbitrary destination. + /// This class allows redirection of logs to gRPC logger. /// </summary> - internal static class GrpcLog + internal static class NativeLogRedirector { static object staticLock = new object(); static GprLogDelegate writeCallback; - static TextWriter dest; [DllImport("grpc_csharp_ext.dll")] static extern void grpcsharp_redirect_log(GprLogDelegate callback); /// <summary> - /// Sets text writer as destination for logs from native gRPC C core library. - /// Only first invocation has effect. + /// Redirects logs from native gRPC C core library to a general logger. /// </summary> - /// <param name="textWriter"></param> - public static void RedirectNativeLogs(TextWriter textWriter) + public static void Redirect() { lock (staticLock) { if (writeCallback == null) { writeCallback = new GprLogDelegate(HandleWrite); - dest = textWriter; grpcsharp_redirect_log(writeCallback); } } @@ -77,13 +73,30 @@ namespace Grpc.Core.Internal { try { - // TODO: DateTime format used here is different than in C core. - dest.WriteLine(string.Format("{0}{1} {2} {3}:{4}: {5}", - Marshal.PtrToStringAnsi(severityStringPtr), DateTime.Now, + var logger = GrpcEnvironment.Logger; + string severityString = Marshal.PtrToStringAnsi(severityStringPtr); + string message = string.Format("{0} {1}:{2}: {3}", threadId, Marshal.PtrToStringAnsi(fileStringPtr), line, - Marshal.PtrToStringAnsi(msgPtr))); + Marshal.PtrToStringAnsi(msgPtr)); + + switch (severityString) + { + case "D": + logger.Debug(message); + break; + case "I": + logger.Info(message); + break; + case "E": + logger.Error(message); + break; + default: + // severity not recognized, default to error. + logger.Error(message); + break; + } } catch (Exception e) { diff --git a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs index 3680b1e791..19f0e3c57f 100644 --- a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs +++ b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs @@ -37,6 +37,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Grpc.Core.Internal; +using Grpc.Core.Logging; using Grpc.Core.Utils; namespace Grpc.Core.Internal @@ -50,6 +51,8 @@ namespace Grpc.Core.Internal where TRequest : class where TResponse : class { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<UnaryServerCallHandler<TRequest, TResponse>>(); + readonly Method<TRequest, TResponse> method; readonly UnaryServerMethod<TRequest, TResponse> handler; @@ -72,7 +75,7 @@ namespace Grpc.Core.Internal var responseStream = new ServerResponseStream<TRequest, TResponse>(asyncCall); Status status; - var context = HandlerUtils.NewContext(newRpc); + var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, asyncCall.CancellationToken); try { Preconditions.CheckArgument(await requestStream.MoveNext()); @@ -85,7 +88,7 @@ namespace Grpc.Core.Internal } catch (Exception e) { - Console.WriteLine("Exception occured in handler: " + e); + Logger.Error(e, "Exception occured in handler."); status = HandlerUtils.StatusFromException(e); } try @@ -104,6 +107,8 @@ namespace Grpc.Core.Internal where TRequest : class where TResponse : class { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<ServerStreamingServerCallHandler<TRequest, TResponse>>(); + readonly Method<TRequest, TResponse> method; readonly ServerStreamingServerMethod<TRequest, TResponse> handler; @@ -126,7 +131,7 @@ namespace Grpc.Core.Internal var responseStream = new ServerResponseStream<TRequest, TResponse>(asyncCall); Status status; - var context = HandlerUtils.NewContext(newRpc); + var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, asyncCall.CancellationToken); try { Preconditions.CheckArgument(await requestStream.MoveNext()); @@ -138,7 +143,7 @@ namespace Grpc.Core.Internal } catch (Exception e) { - Console.WriteLine("Exception occured in handler: " + e); + Logger.Error(e, "Exception occured in handler."); status = HandlerUtils.StatusFromException(e); } @@ -158,6 +163,8 @@ namespace Grpc.Core.Internal where TRequest : class where TResponse : class { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<ClientStreamingServerCallHandler<TRequest, TResponse>>(); + readonly Method<TRequest, TResponse> method; readonly ClientStreamingServerMethod<TRequest, TResponse> handler; @@ -180,7 +187,7 @@ namespace Grpc.Core.Internal var responseStream = new ServerResponseStream<TRequest, TResponse>(asyncCall); Status status; - var context = HandlerUtils.NewContext(newRpc); + var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, asyncCall.CancellationToken); try { var result = await handler(requestStream, context); @@ -196,7 +203,7 @@ namespace Grpc.Core.Internal } catch (Exception e) { - Console.WriteLine("Exception occured in handler: " + e); + Logger.Error(e, "Exception occured in handler."); status = HandlerUtils.StatusFromException(e); } @@ -216,6 +223,8 @@ namespace Grpc.Core.Internal where TRequest : class where TResponse : class { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<DuplexStreamingServerCallHandler<TRequest, TResponse>>(); + readonly Method<TRequest, TResponse> method; readonly DuplexStreamingServerMethod<TRequest, TResponse> handler; @@ -238,7 +247,7 @@ namespace Grpc.Core.Internal var responseStream = new ServerResponseStream<TRequest, TResponse>(asyncCall); Status status; - var context = HandlerUtils.NewContext(newRpc); + var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, asyncCall.CancellationToken); try { await handler(requestStream, responseStream, context); @@ -246,7 +255,7 @@ namespace Grpc.Core.Internal } catch (Exception e) { - Console.WriteLine("Exception occured in handler: " + e); + Logger.Error(e, "Exception occured in handler."); status = HandlerUtils.StatusFromException(e); } try @@ -295,11 +304,13 @@ namespace Grpc.Core.Internal return new Status(StatusCode.Unknown, "Exception was thrown by handler."); } - public static ServerCallContext NewContext(ServerRpcNew newRpc) + public static ServerCallContext NewContext(ServerRpcNew newRpc, string peer, CancellationToken cancellationToken) { + DateTime realtimeDeadline = newRpc.Deadline.ToClockType(GPRClockType.Realtime).ToDateTime(); + return new ServerCallContext( - newRpc.Method, newRpc.Host, newRpc.Deadline.ToDateTime(), - newRpc.RequestMetadata, CancellationToken.None); + newRpc.Method, newRpc.Host, peer, realtimeDeadline, + newRpc.RequestMetadata, cancellationToken); } } } diff --git a/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs index 961180741a..37a4f5256b 100644 --- a/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs @@ -42,7 +42,7 @@ namespace Grpc.Core.Internal internal class ServerCredentialsSafeHandle : SafeHandleZeroIsInvalid { [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)] - static extern ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs); + static extern ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, bool forceClientAuth); [DllImport("grpc_csharp_ext.dll")] static extern void grpcsharp_server_credentials_release(IntPtr credentials); @@ -51,12 +51,13 @@ namespace Grpc.Core.Internal { } - public static ServerCredentialsSafeHandle CreateSslCredentials(string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray) + public static ServerCredentialsSafeHandle CreateSslCredentials(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, bool forceClientAuth) { Preconditions.CheckArgument(keyCertPairCertChainArray.Length == keyCertPairPrivateKeyArray.Length); - return grpcsharp_ssl_server_credentials_create(null, + return grpcsharp_ssl_server_credentials_create(pemRootCerts, keyCertPairCertChainArray, keyCertPairPrivateKeyArray, - new UIntPtr((ulong)keyCertPairCertChainArray.Length)); + new UIntPtr((ulong)keyCertPairCertChainArray.Length), + forceClientAuth); } protected override bool ReleaseHandle() diff --git a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs index 9e1170e6dd..f9b44b1acf 100644 --- a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs @@ -48,7 +48,7 @@ namespace Grpc.Core.Internal static extern ServerSafeHandle grpcsharp_server_create(CompletionQueueSafeHandle cq, ChannelArgsSafeHandle args); [DllImport("grpc_csharp_ext.dll")] - static extern int grpcsharp_server_add_http2_port(ServerSafeHandle server, string addr); + static extern int grpcsharp_server_add_insecure_http2_port(ServerSafeHandle server, string addr); [DllImport("grpc_csharp_ext.dll")] static extern int grpcsharp_server_add_secure_http2_port(ServerSafeHandle server, string addr, ServerCredentialsSafeHandle creds); @@ -77,12 +77,12 @@ namespace Grpc.Core.Internal return grpcsharp_server_create(cq, args); } - public int AddListeningPort(string addr) + public int AddInsecurePort(string addr) { - return grpcsharp_server_add_http2_port(this, addr); + return grpcsharp_server_add_insecure_http2_port(this, addr); } - public int AddListeningPort(string addr, ServerCredentialsSafeHandle credentials) + public int AddSecurePort(string addr, ServerCredentialsSafeHandle credentials) { return grpcsharp_server_add_secure_http2_port(this, addr, credentials); } diff --git a/src/csharp/Grpc.Core/Internal/Timespec.cs b/src/csharp/Grpc.Core/Internal/Timespec.cs index da2819f14d..e83d21f4a4 100644 --- a/src/csharp/Grpc.Core/Internal/Timespec.cs +++ b/src/csharp/Grpc.Core/Internal/Timespec.cs @@ -32,6 +32,8 @@ using System; using System.Runtime.InteropServices; using System.Threading; +using Grpc.Core.Utils; + namespace Grpc.Core.Internal { /// <summary> @@ -40,32 +42,43 @@ namespace Grpc.Core.Internal [StructLayout(LayoutKind.Sequential)] internal struct Timespec { - const int NanosPerSecond = 1000 * 1000 * 1000; - const int NanosPerTick = 100; + const long NanosPerSecond = 1000 * 1000 * 1000; + const long NanosPerTick = 100; + const long TicksPerSecond = NanosPerSecond / NanosPerTick; static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); [DllImport("grpc_csharp_ext.dll")] - static extern Timespec gprsharp_now(); + static extern Timespec gprsharp_now(GPRClockType clockType); + + [DllImport("grpc_csharp_ext.dll")] + static extern Timespec gprsharp_inf_future(GPRClockType clockType); + + [DllImport("grpc_csharp_ext.dll")] + static extern Timespec gprsharp_inf_past(GPRClockType clockType); [DllImport("grpc_csharp_ext.dll")] - static extern Timespec gprsharp_inf_future(); + static extern Timespec gprsharp_convert_clock_type(Timespec t, GPRClockType targetClock); [DllImport("grpc_csharp_ext.dll")] static extern int gprsharp_sizeof_timespec(); - public Timespec(IntPtr tv_sec, int tv_nsec) + public Timespec(IntPtr tv_sec, int tv_nsec) : this(tv_sec, tv_nsec, GPRClockType.Realtime) + { + } + + public Timespec(IntPtr tv_sec, int tv_nsec, GPRClockType clock_type) { this.tv_sec = tv_sec; this.tv_nsec = tv_nsec; - this.clock_type = GPRClockType.Realtime; + this.clock_type = clock_type; } // NOTE: on linux 64bit sizeof(gpr_timespec) = 16, on windows 32bit sizeof(gpr_timespec) = 8 // so IntPtr seems to have the right size to work on both. - public System.IntPtr tv_sec; - public int tv_nsec; - public GPRClockType clock_type; + private System.IntPtr tv_sec; + private int tv_nsec; + private GPRClockType clock_type; /// <summary> /// Timespec a long time in the future. @@ -74,54 +87,164 @@ namespace Grpc.Core.Internal { get { - return gprsharp_inf_future(); + return gprsharp_inf_future(GPRClockType.Realtime); + } + } + + /// <summary> + /// Timespec a long time in the past. + /// </summary> + public static Timespec InfPast + { + get + { + return gprsharp_inf_past(GPRClockType.Realtime); } } + /// <summary> + /// Return Timespec representing the current time. + /// </summary> public static Timespec Now { get { - return gprsharp_now(); + return gprsharp_now(GPRClockType.Realtime); } } - - public DateTime ToDateTime() + + /// <summary> + /// Seconds since unix epoch. + /// </summary> + public IntPtr TimevalSeconds { - return UnixEpoch.AddTicks(tv_sec.ToInt64() * (NanosPerSecond / NanosPerTick) + tv_nsec / NanosPerTick); + get + { + return tv_sec; + } } - internal static int NativeSize + /// <summary> + /// The nanoseconds part of timeval. + /// </summary> + public int TimevalNanos { get { - return gprsharp_sizeof_timespec(); + return tv_nsec; } } /// <summary> - /// Creates a GPR deadline from current instant and given timeout. + /// Converts the timespec to desired clock type. /// </summary> - /// <returns>The from timeout.</returns> - public static Timespec DeadlineFromTimeout(TimeSpan timeout) + public Timespec ToClockType(GPRClockType targetClock) + { + return gprsharp_convert_clock_type(this, targetClock); + } + + /// <summary> + /// Converts Timespec to DateTime. + /// Timespec needs to be of type GPRClockType.Realtime and needs to represent a legal value. + /// DateTime has lower resolution (100ns), so rounding can occurs. + /// Value are always rounded up to the nearest DateTime value in the future. + /// + /// For Timespec.InfFuture or if timespec is after the largest representable DateTime, DateTime.MaxValue is returned. + /// For Timespec.InfPast or if timespec is before the lowest representable DateTime, DateTime.MinValue is returned. + /// + /// Unless DateTime.MaxValue or DateTime.MinValue is returned, the resulting DateTime is always in UTC + /// (DateTimeKind.Utc) + /// </summary> + public DateTime ToDateTime() { - if (timeout == Timeout.InfiniteTimeSpan) + Preconditions.CheckState(tv_nsec >= 0 && tv_nsec < NanosPerSecond); + Preconditions.CheckState(clock_type == GPRClockType.Realtime); + + // fast path for InfFuture + if (this.Equals(InfFuture)) + { + return DateTime.MaxValue; + } + + // fast path for InfPast + if (this.Equals(InfPast)) + { + return DateTime.MinValue; + } + + try + { + // convert nanos to ticks, round up to the nearest tick + long ticksFromNanos = tv_nsec / NanosPerTick + ((tv_nsec % NanosPerTick != 0) ? 1 : 0); + long ticksTotal = checked(tv_sec.ToInt64() * TicksPerSecond + ticksFromNanos); + return UnixEpoch.AddTicks(ticksTotal); + } + catch (OverflowException) + { + // ticks out of long range + return tv_sec.ToInt64() > 0 ? DateTime.MaxValue : DateTime.MinValue; + } + catch (ArgumentOutOfRangeException) + { + // resulting date time would be larger than MaxValue + return tv_sec.ToInt64() > 0 ? DateTime.MaxValue : DateTime.MinValue; + } + } + + /// <summary> + /// Creates DateTime to Timespec. + /// DateTime has to be in UTC (DateTimeKind.Utc) unless it's DateTime.MaxValue or DateTime.MinValue. + /// For DateTime.MaxValue of date time after the largest representable Timespec, Timespec.InfFuture is returned. + /// For DateTime.MinValue of date time before the lowest representable Timespec, Timespec.InfPast is returned. + /// </summary> + /// <returns>The date time.</returns> + /// <param name="dateTime">Date time.</param> + public static Timespec FromDateTime(DateTime dateTime) + { + if (dateTime == DateTime.MaxValue) { return Timespec.InfFuture; } - return Timespec.Now.Add(timeout); + + if (dateTime == DateTime.MinValue) + { + return Timespec.InfPast; + } + + Preconditions.CheckArgument(dateTime.Kind == DateTimeKind.Utc, "dateTime"); + + try + { + TimeSpan timeSpan = dateTime - UnixEpoch; + long ticks = timeSpan.Ticks; + + long seconds = ticks / TicksPerSecond; + int nanos = (int)((ticks % TicksPerSecond) * NanosPerTick); + if (nanos < 0) + { + // correct the result based on C# modulo semantics for negative dividend + seconds--; + nanos += (int)NanosPerSecond; + } + // new IntPtr possibly throws OverflowException + return new Timespec(new IntPtr(seconds), nanos); + } + catch (OverflowException) + { + return dateTime > UnixEpoch ? Timespec.InfFuture : Timespec.InfPast; + } + catch (ArgumentOutOfRangeException) + { + return dateTime > UnixEpoch ? Timespec.InfFuture : Timespec.InfPast; + } } - public Timespec Add(TimeSpan timeSpan) + internal static int NativeSize { - long nanos = (long)tv_nsec + (timeSpan.Ticks % TimeSpan.TicksPerSecond) * NanosPerTick; - long overflow_sec = (nanos > NanosPerSecond) ? 1 : 0; - - Timespec result; - result.tv_nsec = (int)(nanos % NanosPerSecond); - result.tv_sec = new IntPtr(tv_sec.ToInt64() + (timeSpan.Ticks / TimeSpan.TicksPerSecond) + overflow_sec); - result.clock_type = GPRClockType.Realtime; - return result; + get + { + return gprsharp_sizeof_timespec(); + } } } } diff --git a/src/csharp/Grpc.Core/KeyCertificatePair.cs b/src/csharp/Grpc.Core/KeyCertificatePair.cs new file mode 100644 index 0000000000..5def15a656 --- /dev/null +++ b/src/csharp/Grpc.Core/KeyCertificatePair.cs @@ -0,0 +1,83 @@ +#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 Grpc.Core.Internal; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// <summary> + /// Key certificate pair (in PEM encoding). + /// </summary> + public sealed class KeyCertificatePair + { + readonly string certificateChain; + readonly string privateKey; + + /// <summary> + /// Creates a new certificate chain - private key pair. + /// </summary> + /// <param name="certificateChain">PEM encoded certificate chain.</param> + /// <param name="privateKey">PEM encoded private key.</param> + public KeyCertificatePair(string certificateChain, string privateKey) + { + this.certificateChain = Preconditions.CheckNotNull(certificateChain); + this.privateKey = Preconditions.CheckNotNull(privateKey); + } + + /// <summary> + /// PEM encoded certificate chain. + /// </summary> + public string CertificateChain + { + get + { + return certificateChain; + } + } + + /// <summary> + /// PEM encoded private key. + /// </summary> + public string PrivateKey + { + get + { + return privateKey; + } + } + } +} diff --git a/src/csharp/Grpc.Core/Logging/ConsoleLogger.cs b/src/csharp/Grpc.Core/Logging/ConsoleLogger.cs new file mode 100644 index 0000000000..c67765c78d --- /dev/null +++ b/src/csharp/Grpc.Core/Logging/ConsoleLogger.cs @@ -0,0 +1,103 @@ +#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; + +namespace Grpc.Core.Logging +{ + /// <summary>Logger that logs to System.Console.</summary> + public class ConsoleLogger : ILogger + { + readonly Type forType; + readonly string forTypeString; + + public ConsoleLogger() : this(null) + { + } + + private ConsoleLogger(Type forType) + { + this.forType = forType; + this.forTypeString = forType != null ? forType.FullName + " " : ""; + } + + public ILogger ForType<T>() + { + if (typeof(T) == forType) + { + return this; + } + return new ConsoleLogger(typeof(T)); + } + + public void Debug(string message, params object[] formatArgs) + { + Log("D", message, formatArgs); + } + + public void Info(string message, params object[] formatArgs) + { + Log("I", message, formatArgs); + } + + public void Warning(string message, params object[] formatArgs) + { + Log("W", message, formatArgs); + } + + public void Warning(Exception exception, string message, params object[] formatArgs) + { + Log("W", message + " " + exception, formatArgs); + } + + public void Error(string message, params object[] formatArgs) + { + Log("E", message, formatArgs); + } + + public void Error(Exception exception, string message, params object[] formatArgs) + { + Log("E", message + " " + exception, formatArgs); + } + + private void Log(string severityString, string message, object[] formatArgs) + { + Console.Error.WriteLine("{0}{1} {2}{3}", + severityString, + DateTime.Now, + forTypeString, + string.Format(message, formatArgs)); + } + } +} diff --git a/src/csharp/Grpc.Core/Logging/ILogger.cs b/src/csharp/Grpc.Core/Logging/ILogger.cs new file mode 100644 index 0000000000..0d58f133e3 --- /dev/null +++ b/src/csharp/Grpc.Core/Logging/ILogger.cs @@ -0,0 +1,57 @@ +#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; + +namespace Grpc.Core.Logging +{ + /// <summary>For logging messages.</summary> + public interface ILogger + { + /// <summary>Returns a logger associated with the specified type.</summary> + ILogger ForType<T>(); + + void Debug(string message, params object[] formatArgs); + + void Info(string message, params object[] formatArgs); + + void Warning(string message, params object[] formatArgs); + + void Warning(Exception exception, string message, params object[] formatArgs); + + void Error(string message, params object[] formatArgs); + + void Error(Exception exception, string message, params object[] formatArgs); + } +} diff --git a/src/csharp/Grpc.Core/Metadata.cs b/src/csharp/Grpc.Core/Metadata.cs index 2f308cbb11..6fd0a7109d 100644 --- a/src/csharp/Grpc.Core/Metadata.cs +++ b/src/csharp/Grpc.Core/Metadata.cs @@ -32,7 +32,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Collections.Immutable; using System.Collections.Specialized; using System.Runtime.InteropServices; using System.Text; diff --git a/src/csharp/Grpc.Core/Method.cs b/src/csharp/Grpc.Core/Method.cs index 77d36191c3..cc047ac9f8 100644 --- a/src/csharp/Grpc.Core/Method.cs +++ b/src/csharp/Grpc.Core/Method.cs @@ -53,16 +53,20 @@ namespace Grpc.Core public class Method<TRequest, TResponse> { readonly MethodType type; + readonly string serviceName; readonly string name; readonly Marshaller<TRequest> requestMarshaller; readonly Marshaller<TResponse> responseMarshaller; + readonly string fullName; - public Method(MethodType type, string name, Marshaller<TRequest> requestMarshaller, Marshaller<TResponse> responseMarshaller) + public Method(MethodType type, string serviceName, string name, Marshaller<TRequest> requestMarshaller, Marshaller<TResponse> responseMarshaller) { this.type = type; - this.name = name; - this.requestMarshaller = requestMarshaller; - this.responseMarshaller = responseMarshaller; + this.serviceName = Preconditions.CheckNotNull(serviceName); + this.name = Preconditions.CheckNotNull(name); + this.requestMarshaller = Preconditions.CheckNotNull(requestMarshaller); + this.responseMarshaller = Preconditions.CheckNotNull(responseMarshaller); + this.fullName = GetFullName(serviceName); } public MethodType Type @@ -72,6 +76,14 @@ namespace Grpc.Core return this.type; } } + + public string ServiceName + { + get + { + return this.serviceName; + } + } public string Name { @@ -97,6 +109,14 @@ namespace Grpc.Core } } + public string FullName + { + get + { + return this.fullName; + } + } + /// <summary> /// Gets full name of the method including the service name. /// </summary> diff --git a/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs index 2b3d7530f2..29db85d7aa 100644 --- a/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs @@ -10,4 +10,12 @@ using System.Runtime.CompilerServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] +#if SIGNED +[assembly: InternalsVisibleTo("Grpc.Core.Tests,PublicKey=" + + "00240000048000009400000006020000002400005253413100040000010001002f5797a92c6fcde81bd4098f43" + + "0442bb8e12768722de0b0cb1b15e955b32a11352740ee59f2c94c48edc8e177d1052536b8ac651bce11ce5da3a" + + "27fc95aff3dc604a6971417453f9483c7b5e836756d5b271bf8f2403fe186e31956148c03d804487cf642f8cc0" + + "71394ee9672dfe5b55ea0f95dfd5a7f77d22c962ccf51320d3")] +#else [assembly: InternalsVisibleTo("Grpc.Core.Tests")] +#endif
\ No newline at end of file diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs index fd30735359..eb5b043d1c 100644 --- a/src/csharp/Grpc.Core/Server.cs +++ b/src/csharp/Grpc.Core/Server.cs @@ -32,12 +32,13 @@ #endregion using System; -using System.Collections.Concurrent; +using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading.Tasks; using Grpc.Core.Internal; +using Grpc.Core.Logging; using Grpc.Core.Utils; namespace Grpc.Core @@ -47,16 +48,17 @@ namespace Grpc.Core /// </summary> public class Server { - /// <summary> - /// Pass this value as port to have the server choose an unused listening port for you. - /// </summary> - public const int PickUnusedPort = 0; + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<Server>(); + readonly ServiceDefinitionCollection serviceDefinitions; + readonly ServerPortCollection ports; readonly GrpcEnvironment environment; readonly List<ChannelOption> options; readonly ServerSafeHandle handle; readonly object myLock = new object(); + readonly List<ServerServiceDefinition> serviceDefinitionsList = new List<ServerServiceDefinition>(); + readonly List<ServerPort> serverPortList = new List<ServerPort>(); readonly Dictionary<string, IServerCallHandler> callHandlers = new Dictionary<string, IServerCallHandler>(); readonly TaskCompletionSource<object> shutdownTcs = new TaskCompletionSource<object>(); @@ -69,6 +71,8 @@ namespace Grpc.Core /// <param name="options">Channel options.</param> public Server(IEnumerable<ChannelOption> options = null) { + this.serviceDefinitions = new ServiceDefinitionCollection(this); + this.ports = new ServerPortCollection(this); this.environment = GrpcEnvironment.GetInstance(); this.options = options != null ? new List<ChannelOption>(options) : new List<ChannelOption>(); using (var channelArgs = ChannelOptions.CreateChannelArgs(this.options)) @@ -78,45 +82,27 @@ namespace Grpc.Core } /// <summary> - /// Adds a service definition to the server. This is how you register - /// handlers for a service with the server. - /// Only call this before Start(). + /// Services that will be exported by the server once started. Register a service with this + /// server by adding its definition to this collection. /// </summary> - public void AddServiceDefinition(ServerServiceDefinition serviceDefinition) + public ServiceDefinitionCollection Services { - lock (myLock) + get { - Preconditions.CheckState(!startRequested); - foreach (var entry in serviceDefinition.CallHandlers) - { - callHandlers.Add(entry.Key, entry.Value); - } + return serviceDefinitions; } } /// <summary> - /// Add a non-secure port on which server should listen. - /// Only call this before Start(). + /// Ports on which the server will listen once started. Register a port with this + /// server by adding its definition to this collection. /// </summary> - /// <returns>The port on which server will be listening.</returns> - /// <param name="host">the host</param> - /// <param name="port">the port. If zero, an unused port is chosen automatically.</param> - public int AddListeningPort(string host, int port) + public ServerPortCollection Ports { - return AddListeningPortInternal(host, port, null); - } - - /// <summary> - /// Add a non-secure port on which server should listen. - /// Only call this before Start(). - /// </summary> - /// <returns>The port on which server will be listening.</returns> - /// <param name="host">the host</param> - /// <param name="port">the port. If zero, an unused port is chosen automatically.</param> - public int AddListeningPort(string host, int port, ServerCredentials credentials) - { - Preconditions.CheckNotNull(credentials); - return AddListeningPortInternal(host, port, credentials); + get + { + return ports; + } } /// <summary> @@ -183,23 +169,47 @@ namespace Grpc.Core handle.Dispose(); } - private int AddListeningPortInternal(string host, int port, ServerCredentials credentials) + /// <summary> + /// Adds a service definition. + /// </summary> + private void AddServiceDefinitionInternal(ServerServiceDefinition serviceDefinition) { lock (myLock) { - Preconditions.CheckState(!startRequested); - var address = string.Format("{0}:{1}", host, port); - if (credentials != null) + Preconditions.CheckState(!startRequested); + foreach (var entry in serviceDefinition.CallHandlers) { - using (var nativeCredentials = credentials.ToNativeCredentials()) - { - return handle.AddListeningPort(address, nativeCredentials); - } + callHandlers.Add(entry.Key, entry.Value); } - else + serviceDefinitionsList.Add(serviceDefinition); + } + } + + /// <summary> + /// Adds a listening port. + /// </summary> + private int AddPortInternal(ServerPort serverPort) + { + lock (myLock) + { + Preconditions.CheckNotNull(serverPort.Credentials); + Preconditions.CheckState(!startRequested); + var address = string.Format("{0}:{1}", serverPort.Host, serverPort.Port); + int boundPort; + using (var nativeCredentials = serverPort.Credentials.ToNativeCredentials()) { - return handle.AddListeningPort(address); + if (nativeCredentials != null) + { + boundPort = handle.AddSecurePort(address, nativeCredentials); + } + else + { + boundPort = handle.AddInsecurePort(address); + } } + var newServerPort = new ServerPort(serverPort, boundPort); + this.serverPortList.Add(newServerPort); + return boundPort; } } @@ -233,7 +243,7 @@ namespace Grpc.Core } catch (Exception e) { - Console.WriteLine("Exception while handling RPC: " + e); + Logger.Warning(e, "Exception while handling RPC."); } } @@ -263,5 +273,82 @@ namespace Grpc.Core { shutdownTcs.SetResult(null); } + + /// <summary> + /// Collection of service definitions. + /// </summary> + public class ServiceDefinitionCollection : IEnumerable<ServerServiceDefinition> + { + readonly Server server; + + internal ServiceDefinitionCollection(Server server) + { + this.server = server; + } + + /// <summary> + /// Adds a service definition to the server. This is how you register + /// handlers for a service with the server. Only call this before Start(). + /// </summary> + public void Add(ServerServiceDefinition serviceDefinition) + { + server.AddServiceDefinitionInternal(serviceDefinition); + } + + public IEnumerator<ServerServiceDefinition> GetEnumerator() + { + return server.serviceDefinitionsList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return server.serviceDefinitionsList.GetEnumerator(); + } + } + + /// <summary> + /// Collection of server ports. + /// </summary> + public class ServerPortCollection : IEnumerable<ServerPort> + { + readonly Server server; + + internal ServerPortCollection(Server server) + { + this.server = server; + } + + /// <summary> + /// Adds a new port on which server should listen. + /// Only call this before Start(). + /// <returns>The port on which server will be listening.</returns> + /// </summary> + public int Add(ServerPort serverPort) + { + return server.AddPortInternal(serverPort); + } + + /// <summary> + /// Adds a new port on which server should listen. + /// <returns>The port on which server will be listening.</returns> + /// </summary> + /// <param name="host">the host</param> + /// <param name="port">the port. If zero, an unused port is chosen automatically.</param> + /// <param name="credentials">credentials to use to secure this port.</param> + public int Add(string host, int port, ServerCredentials credentials) + { + return Add(new ServerPort(host, port, credentials)); + } + + public IEnumerator<ServerPort> GetEnumerator() + { + return server.serverPortList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return server.serverPortList.GetEnumerator(); + } + } } } diff --git a/src/csharp/Grpc.Core/ServerCallContext.cs b/src/csharp/Grpc.Core/ServerCallContext.cs index 17a2eefd07..032b1390db 100644 --- a/src/csharp/Grpc.Core/ServerCallContext.cs +++ b/src/csharp/Grpc.Core/ServerCallContext.cs @@ -47,6 +47,7 @@ namespace Grpc.Core private readonly string method; private readonly string host; + private readonly string peer; private readonly DateTime deadline; private readonly Metadata requestHeaders; private readonly CancellationToken cancellationToken; @@ -54,16 +55,17 @@ namespace Grpc.Core private Status status = Status.DefaultSuccess; - public ServerCallContext(string method, string host, DateTime deadline, Metadata requestHeaders, CancellationToken cancellationToken) + public ServerCallContext(string method, string host, string peer, DateTime deadline, Metadata requestHeaders, CancellationToken cancellationToken) { this.method = method; this.host = host; + this.peer = peer; this.deadline = deadline; this.requestHeaders = requestHeaders; this.cancellationToken = cancellationToken; } - /// <summary> Name of method called in this RPC. </summary> + /// <summary>Name of method called in this RPC.</summary> public string Method { get @@ -72,7 +74,7 @@ namespace Grpc.Core } } - /// <summary> Name of host called in this RPC. </summary> + /// <summary>Name of host called in this RPC.</summary> public string Host { get @@ -81,7 +83,16 @@ namespace Grpc.Core } } - /// <summary> Deadline for this RPC. </summary> + /// <summary>Address of the remote endpoint in URI format.</summary> + public string Peer + { + get + { + return this.peer; + } + } + + /// <summary>Deadline for this RPC.</summary> public DateTime Deadline { get @@ -90,7 +101,7 @@ namespace Grpc.Core } } - /// <summary> Initial metadata sent by client. </summary> + /// <summary>Initial metadata sent by client.</summary> public Metadata RequestHeaders { get @@ -99,8 +110,7 @@ namespace Grpc.Core } } - // TODO(jtattermusch): support signalling cancellation. - /// <summary> Cancellation token signals when call is cancelled. </summary> + ///<summary>Cancellation token signals when call is cancelled.</summary> public CancellationToken CancellationToken { get @@ -109,7 +119,7 @@ namespace Grpc.Core } } - /// <summary> Trailers to send back to client after RPC finishes.</summary> + /// <summary>Trailers to send back to client after RPC finishes.</summary> public Metadata ResponseTrailers { get diff --git a/src/csharp/Grpc.Core/ServerCredentials.cs b/src/csharp/Grpc.Core/ServerCredentials.cs index ab7d0b4914..c11a1ede08 100644 --- a/src/csharp/Grpc.Core/ServerCredentials.cs +++ b/src/csharp/Grpc.Core/ServerCredentials.cs @@ -33,8 +33,8 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using Grpc.Core.Internal; +using Grpc.Core.Utils; namespace Grpc.Core { @@ -43,67 +43,118 @@ namespace Grpc.Core /// </summary> public abstract class ServerCredentials { + static readonly ServerCredentials InsecureInstance = new InsecureServerCredentialsImpl(); + + /// <summary> + /// Returns instance of credential that provides no security and + /// will result in creating an unsecure server port with no encryption whatsoever. + /// </summary> + public static ServerCredentials Insecure + { + get + { + return InsecureInstance; + } + } + /// <summary> /// Creates native object for the credentials. /// </summary> /// <returns>The native credentials.</returns> internal abstract ServerCredentialsSafeHandle ToNativeCredentials(); + + private sealed class InsecureServerCredentialsImpl : ServerCredentials + { + internal override ServerCredentialsSafeHandle ToNativeCredentials() + { + return null; + } + } } /// <summary> - /// Key certificate pair (in PEM encoding). + /// Server-side SSL credentials. /// </summary> - public class KeyCertificatePair + public class SslServerCredentials : ServerCredentials { - readonly string certChain; - readonly string privateKey; + readonly IList<KeyCertificatePair> keyCertificatePairs; + readonly string rootCertificates; + readonly bool forceClientAuth; - public KeyCertificatePair(string certChain, string privateKey) + /// <summary> + /// Creates server-side SSL credentials. + /// </summary> + /// <param name="keyCertificatePairs">Key-certificates to use.</param> + /// <param name="rootCertificates">PEM encoded client root certificates used to authenticate client.</param> + /// <param name="forceClientAuth">If true, client will be rejected unless it proves its unthenticity using against rootCertificates.</param> + public SslServerCredentials(IEnumerable<KeyCertificatePair> keyCertificatePairs, string rootCertificates, bool forceClientAuth) { - this.certChain = certChain; - this.privateKey = privateKey; + this.keyCertificatePairs = new List<KeyCertificatePair>(keyCertificatePairs).AsReadOnly(); + Preconditions.CheckArgument(this.keyCertificatePairs.Count > 0, + "At least one KeyCertificatePair needs to be provided"); + if (forceClientAuth) + { + Preconditions.CheckNotNull(rootCertificates, + "Cannot force client authentication unless you provide rootCertificates."); + } + this.rootCertificates = rootCertificates; + this.forceClientAuth = forceClientAuth; } - public string CertChain + /// <summary> + /// Creates server-side SSL credentials. + /// This constructor should be use if you do not wish to autheticate client + /// using client root certificates. + /// </summary> + /// <param name="keyCertificatePairs">Key-certificates to use.</param> + public SslServerCredentials(IEnumerable<KeyCertificatePair> keyCertificatePairs) : this(keyCertificatePairs, null, false) + { + } + + /// <summary> + /// Key-certificate pairs. + /// </summary> + public IList<KeyCertificatePair> KeyCertificatePairs { get { - return certChain; + return this.keyCertificatePairs; } } - public string PrivateKey + /// <summary> + /// PEM encoded client root certificates. + /// </summary> + public string RootCertificates { get { - return privateKey; + return this.rootCertificates; } } - } - - /// <summary> - /// Server-side SSL credentials. - /// </summary> - public class SslServerCredentials : ServerCredentials - { - ImmutableList<KeyCertificatePair> keyCertPairs; - public SslServerCredentials(ImmutableList<KeyCertificatePair> keyCertPairs) + /// <summary> + /// If true, the authenticity of client check will be enforced. + /// </summary> + public bool ForceClientAuthentication { - this.keyCertPairs = keyCertPairs; + get + { + return this.forceClientAuth; + } } internal override ServerCredentialsSafeHandle ToNativeCredentials() { - int count = keyCertPairs.Count; + int count = keyCertificatePairs.Count; string[] certChains = new string[count]; string[] keys = new string[count]; for (int i = 0; i < count; i++) { - certChains[i] = keyCertPairs[i].CertChain; - keys[i] = keyCertPairs[i].PrivateKey; + certChains[i] = keyCertificatePairs[i].CertificateChain; + keys[i] = keyCertificatePairs[i].PrivateKey; } - return ServerCredentialsSafeHandle.CreateSslCredentials(certChains, keys); + return ServerCredentialsSafeHandle.CreateSslCredentials(rootCertificates, certChains, keys, forceClientAuth); } } } diff --git a/src/csharp/Grpc.Core/ServerPort.cs b/src/csharp/Grpc.Core/ServerPort.cs new file mode 100644 index 0000000000..55e4bd0062 --- /dev/null +++ b/src/csharp/Grpc.Core/ServerPort.cs @@ -0,0 +1,120 @@ +#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 Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// <summary> + /// A port exposed by a server. + /// </summary> + public class ServerPort + { + /// <summary> + /// Pass this value as port to have the server choose an unused listening port for you. + /// Ports added to a server will contain the bound port in their <see cref="BoundPort"/> property. + /// </summary> + public const int PickUnused = 0; + + readonly string host; + readonly int port; + readonly ServerCredentials credentials; + readonly int boundPort; + + /// <summary> + /// Creates a new port on which server should listen. + /// </summary> + /// <returns>The port on which server will be listening.</returns> + /// <param name="host">the host</param> + /// <param name="port">the port. If zero, an unused port is chosen automatically.</param> + /// <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.port = port; + this.credentials = Preconditions.CheckNotNull(credentials); + } + + /// <summary> + /// Creates a port from an existing <c>ServerPort</c> instance and boundPort value. + /// </summary> + internal ServerPort(ServerPort serverPort, int boundPort) + { + this.host = serverPort.host; + this.port = serverPort.port; + this.credentials = serverPort.credentials; + this.boundPort = boundPort; + } + + /// <value>The host.</value> + public string Host + { + get + { + return host; + } + } + + /// <value>The port.</value> + public int Port + { + get + { + return port; + } + } + + /// <value>The server credentials.</value> + public ServerCredentials Credentials + { + get + { + return credentials; + } + } + + /// <value> + /// The port actually bound by the server. This is useful if you let server + /// pick port automatically. <see cref="PickUnused"/> + /// </value> + public int BoundPort + { + get + { + return boundPort; + } + } + } +} diff --git a/src/csharp/Grpc.Core/ServerServiceDefinition.cs b/src/csharp/Grpc.Core/ServerServiceDefinition.cs index b180186c12..a00d156e52 100644 --- a/src/csharp/Grpc.Core/ServerServiceDefinition.cs +++ b/src/csharp/Grpc.Core/ServerServiceDefinition.cs @@ -33,7 +33,7 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; +using System.Collections.ObjectModel; using Grpc.Core.Internal; namespace Grpc.Core @@ -43,14 +43,14 @@ namespace Grpc.Core /// </summary> public class ServerServiceDefinition { - readonly ImmutableDictionary<string, IServerCallHandler> callHandlers; + readonly ReadOnlyDictionary<string, IServerCallHandler> callHandlers; - private ServerServiceDefinition(ImmutableDictionary<string, IServerCallHandler> callHandlers) + private ServerServiceDefinition(Dictionary<string, IServerCallHandler> callHandlers) { - this.callHandlers = callHandlers; + this.callHandlers = new ReadOnlyDictionary<string, IServerCallHandler>(callHandlers); } - internal ImmutableDictionary<string, IServerCallHandler> CallHandlers + internal IDictionary<string, IServerCallHandler> CallHandlers { get { @@ -115,7 +115,7 @@ namespace Grpc.Core public ServerServiceDefinition Build() { - return new ServerServiceDefinition(callHandlers.ToImmutableDictionary()); + return new ServerServiceDefinition(callHandlers); } } } diff --git a/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs b/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs index 4180d98938..82653c3a1f 100644 --- a/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs +++ b/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs @@ -46,13 +46,15 @@ namespace Grpc.Core.Utils /// </summary> public static void RunBenchmark(int warmupIterations, int benchmarkIterations, Action action) { - Console.WriteLine("Warmup iterations: " + warmupIterations); + var logger = GrpcEnvironment.Logger; + + logger.Info("Warmup iterations: {0}", warmupIterations); for (int i = 0; i < warmupIterations; i++) { action(); } - Console.WriteLine("Benchmark iterations: " + benchmarkIterations); + logger.Info("Benchmark iterations: {0}", benchmarkIterations); var stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < benchmarkIterations; i++) @@ -60,8 +62,8 @@ namespace Grpc.Core.Utils action(); } stopwatch.Stop(); - Console.WriteLine("Elapsed time: " + stopwatch.ElapsedMilliseconds + "ms"); - Console.WriteLine("Ops per second: " + (int)((double)benchmarkIterations * 1000 / stopwatch.ElapsedMilliseconds)); + logger.Info("Elapsed time: {0}ms", stopwatch.ElapsedMilliseconds); + logger.Info("Ops per second: {0}", (int)((double)benchmarkIterations * 1000 / stopwatch.ElapsedMilliseconds)); } } } diff --git a/src/csharp/Grpc.Core/packages.config b/src/csharp/Grpc.Core/packages.config index 6cdcdf2656..9b12b9b096 100644 --- a/src/csharp/Grpc.Core/packages.config +++ b/src/csharp/Grpc.Core/packages.config @@ -3,5 +3,4 @@ <package id="grpc.dependencies.openssl.redist" version="1.0.2.2" targetFramework="net45" /> <package id="grpc.dependencies.zlib.redist" version="1.2.8.9" targetFramework="net45" /> <package id="Ix-Async" version="1.2.3" targetFramework="net45" /> - <package id="System.Collections.Immutable" version="1.1.36" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj b/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj index 5d5401593d..b603e3af3c 100644 --- a/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj +++ b/src/csharp/Grpc.Examples.MathClient/Grpc.Examples.MathClient.csproj @@ -2,7 +2,7 @@ <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> - <Platform Condition=" '$(Platform)' == '' ">x86</Platform> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <ProductVersion>10.0.0</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}</ProjectGuid> @@ -11,7 +11,7 @@ <AssemblyName>MathClient</AssemblyName> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> @@ -19,17 +19,22 @@ <DefineConstants>DEBUG;</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <Externalconsole>true</Externalconsole> - <PlatformTarget>x86</PlatformTarget> </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> - <DebugType>full</DebugType> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <Externalconsole>true</Externalconsole> - <PlatformTarget>x86</PlatformTarget> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\ReleaseSigned</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> </PropertyGroup> <ItemGroup> <Reference Include="System" /> diff --git a/src/csharp/Grpc.Examples.MathClient/MathClient.cs b/src/csharp/Grpc.Examples.MathClient/MathClient.cs index cfe2a06916..f9839d99f1 100644 --- a/src/csharp/Grpc.Examples.MathClient/MathClient.cs +++ b/src/csharp/Grpc.Examples.MathClient/MathClient.cs @@ -39,7 +39,7 @@ namespace math { public static void Main(string[] args) { - using (Channel channel = new Channel("127.0.0.1", 23456)) + using (Channel channel = new Channel("127.0.0.1", 23456, Credentials.Insecure)) { Math.IMathClient client = new Math.MathClient(channel); MathExamples.DivExample(client); diff --git a/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj b/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj index 677d87da20..5f74b58773 100644 --- a/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj +++ b/src/csharp/Grpc.Examples.MathServer/Grpc.Examples.MathServer.csproj @@ -2,7 +2,7 @@ <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> - <Platform Condition=" '$(Platform)' == '' ">x86</Platform> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <ProductVersion>10.0.0</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{BF62FE08-373A-43D6-9D73-41CAA38B7011}</ProjectGuid> @@ -11,7 +11,7 @@ <AssemblyName>MathServer</AssemblyName> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> @@ -19,17 +19,22 @@ <DefineConstants>DEBUG;</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <Externalconsole>true</Externalconsole> - <PlatformTarget>x86</PlatformTarget> </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> - <DebugType>full</DebugType> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <Externalconsole>true</Externalconsole> - <PlatformTarget>x86</PlatformTarget> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\ReleaseSigned</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> </PropertyGroup> <ItemGroup> <Reference Include="System" /> diff --git a/src/csharp/Grpc.Examples.MathServer/MathServer.cs b/src/csharp/Grpc.Examples.MathServer/MathServer.cs index f440985112..5f7e717b0c 100644 --- a/src/csharp/Grpc.Examples.MathServer/MathServer.cs +++ b/src/csharp/Grpc.Examples.MathServer/MathServer.cs @@ -38,16 +38,19 @@ namespace math { class MainClass { + const string Host = "0.0.0.0"; + const int Port = 23456; + public static void Main(string[] args) { - string host = "0.0.0.0"; - - Server server = new Server(); - server.AddServiceDefinition(Math.BindService(new MathServiceImpl())); - int port = server.AddListeningPort(host, 23456); + Server server = new Server + { + Services = { Math.BindService(new MathServiceImpl()) }, + Ports = { { Host, Port, ServerCredentials.Insecure } } + }; server.Start(); - Console.WriteLine("MathServer listening on port " + port); + Console.WriteLine("MathServer listening on port " + Port); Console.WriteLine("Press any key to stop the server..."); Console.ReadKey(); diff --git a/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj b/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj index d59d7515d1..9a8f780b24 100644 --- a/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj +++ b/src/csharp/Grpc.Examples.Tests/Grpc.Examples.Tests.csproj @@ -19,15 +19,22 @@ <DefineConstants>DEBUG;</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <ConsolePause>false</ConsolePause> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> - <DebugType>full</DebugType> + <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <ConsolePause>false</ConsolePause> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\ReleaseSigned</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> </PropertyGroup> <ItemGroup> <Reference Include="nunit.framework"> diff --git a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs index 7a957c5b6f..08aece7ef2 100644 --- a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs +++ b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs @@ -33,6 +33,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Grpc.Core; @@ -46,7 +47,7 @@ namespace math.Tests /// </summary> public class MathClientServerTest { - string host = "localhost"; + const string Host = "localhost"; Server server; Channel channel; Math.MathClient client; @@ -54,19 +55,14 @@ namespace math.Tests [TestFixtureSetUp] public void Init() { - server = new Server(); - server.AddServiceDefinition(Math.BindService(new MathServiceImpl())); - int port = server.AddListeningPort(host, Server.PickUnusedPort); - server.Start(); - channel = new Channel(host, port); - client = Math.NewClient(channel); - - // TODO(jtattermusch): get rid of the custom header here once we have dedicated tests - // for header support. - client.HeaderInterceptor = (metadata) => + server = new Server { - metadata.Add(new Metadata.Entry("customHeader", "abcdef")); + Services = { Math.BindService(new MathServiceImpl()) }, + Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } }; + server.Start(); + channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure); + client = Math.NewClient(channel); } [TestFixtureTearDown] @@ -108,69 +104,105 @@ namespace math.Tests } [Test] - public void DivAsync() + public async Task DivAsync() + { + DivReply response = await client.DivAsync(new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build()); + Assert.AreEqual(3, response.Quotient); + Assert.AreEqual(1, response.Remainder); + } + + [Test] + public async Task Fib() { - Task.Run(async () => + using (var call = client.Fib(new FibArgs.Builder { Limit = 6 }.Build())) { - DivReply response = await client.DivAsync(new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build()); - Assert.AreEqual(3, response.Quotient); - Assert.AreEqual(1, response.Remainder); - }).Wait(); + var responses = await call.ResponseStream.ToList(); + CollectionAssert.AreEqual(new List<long> { 1, 1, 2, 3, 5, 8 }, + responses.ConvertAll((n) => n.Num_)); + } } [Test] - public void Fib() + public async Task FibWithCancel() { - Task.Run(async () => + var cts = new CancellationTokenSource(); + + using (var call = client.Fib(new FibArgs.Builder { Limit = 0 }.Build(), + cancellationToken: cts.Token)) { - using (var call = client.Fib(new FibArgs.Builder { Limit = 6 }.Build())) + List<long> responses = new List<long>(); + + try { - var responses = await call.ResponseStream.ToList(); - CollectionAssert.AreEqual(new List<long> { 1, 1, 2, 3, 5, 8 }, - responses.ConvertAll((n) => n.Num_)); + while (await call.ResponseStream.MoveNext()) + { + if (responses.Count == 0) + { + cts.CancelAfter(500); // make sure we cancel soon + } + responses.Add(call.ResponseStream.Current.Num_); + } + Assert.Fail(); } - }).Wait(); + catch (RpcException e) + { + Assert.IsTrue(responses.Count > 0); + Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode); + } + } } - // TODO: test Fib with limit=0 and cancellation [Test] - public void Sum() + public async Task FibWithDeadline() { - Task.Run(async () => + using (var call = client.Fib(new FibArgs.Builder { Limit = 0 }.Build(), + deadline: DateTime.UtcNow.AddMilliseconds(500))) { - using (var call = client.Sum()) + try { - var numbers = new List<long> { 10, 20, 30 }.ConvertAll( - n => Num.CreateBuilder().SetNum_(n).Build()); - - await call.RequestStream.WriteAll(numbers); - var result = await call.ResponseAsync; - Assert.AreEqual(60, result.Num_); + await call.ResponseStream.ToList(); + Assert.Fail(); + } + catch (RpcException e) + { + Assert.AreEqual(StatusCode.DeadlineExceeded, e.Status.StatusCode); } - }).Wait(); + } } + // TODO: test Fib with limit=0 and cancellation [Test] - public void DivMany() + public async Task Sum() { - Task.Run(async () => + using (var call = client.Sum()) { - var divArgsList = new List<DivArgs> - { - new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build(), - new DivArgs.Builder { Dividend = 100, Divisor = 21 }.Build(), - new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build() - }; + var numbers = new List<long> { 10, 20, 30 }.ConvertAll( + n => Num.CreateBuilder().SetNum_(n).Build()); - using (var call = client.DivMany()) - { - await call.RequestStream.WriteAll(divArgsList); - var result = await call.ResponseStream.ToList(); + await call.RequestStream.WriteAll(numbers); + var result = await call.ResponseAsync; + Assert.AreEqual(60, result.Num_); + } + } - CollectionAssert.AreEqual(new long[] { 3, 4, 3 }, result.ConvertAll((divReply) => divReply.Quotient)); - CollectionAssert.AreEqual(new long[] { 1, 16, 1 }, result.ConvertAll((divReply) => divReply.Remainder)); - } - }).Wait(); + [Test] + public async Task DivMany() + { + var divArgsList = new List<DivArgs> + { + new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build(), + new DivArgs.Builder { Dividend = 100, Divisor = 21 }.Build(), + new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build() + }; + + using (var call = client.DivMany()) + { + await call.RequestStream.WriteAll(divArgsList); + var result = await call.ResponseStream.ToList(); + + 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/Grpc.Examples.csproj b/src/csharp/Grpc.Examples/Grpc.Examples.csproj index eaf24a253c..c1aa40500e 100644 --- a/src/csharp/Grpc.Examples/Grpc.Examples.csproj +++ b/src/csharp/Grpc.Examples/Grpc.Examples.csproj @@ -19,15 +19,22 @@ <DefineConstants>DEBUG;</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <ConsolePause>false</ConsolePause> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> - <DebugType>full</DebugType> + <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <ConsolePause>false</ConsolePause> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\ReleaseSigned</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> </PropertyGroup> <ItemGroup> <Reference Include="System" /> diff --git a/src/csharp/Grpc.Examples/MathGrpc.cs b/src/csharp/Grpc.Examples/MathGrpc.cs index ef787cf1d8..4941ff35f7 100644 --- a/src/csharp/Grpc.Examples/MathGrpc.cs +++ b/src/csharp/Grpc.Examples/MathGrpc.cs @@ -19,24 +19,28 @@ namespace math { static readonly Method<global::math.DivArgs, global::math.DivReply> __Method_Div = new Method<global::math.DivArgs, global::math.DivReply>( MethodType.Unary, + __ServiceName, "Div", __Marshaller_DivArgs, __Marshaller_DivReply); static readonly Method<global::math.DivArgs, global::math.DivReply> __Method_DivMany = new Method<global::math.DivArgs, global::math.DivReply>( MethodType.DuplexStreaming, + __ServiceName, "DivMany", __Marshaller_DivArgs, __Marshaller_DivReply); static readonly Method<global::math.FibArgs, global::math.Num> __Method_Fib = new Method<global::math.FibArgs, global::math.Num>( MethodType.ServerStreaming, + __ServiceName, "Fib", __Marshaller_FibArgs, __Marshaller_Num); static readonly Method<global::math.Num, global::math.Num> __Method_Sum = new Method<global::math.Num, global::math.Num>( MethodType.ClientStreaming, + __ServiceName, "Sum", __Marshaller_Num, __Marshaller_Num); @@ -44,11 +48,16 @@ namespace math { // client interface public interface IMathClient { - global::math.DivReply Div(global::math.DivArgs request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); - AsyncUnaryCall<global::math.DivReply> DivAsync(global::math.DivArgs request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); - AsyncDuplexStreamingCall<global::math.DivArgs, global::math.DivReply> DivMany(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); - AsyncServerStreamingCall<global::math.Num> Fib(global::math.FibArgs request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); - AsyncClientStreamingCall<global::math.Num, global::math.Num> Sum(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); + global::math.DivReply Div(global::math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + global::math.DivReply Div(global::math.DivArgs request, CallOptions options); + AsyncUnaryCall<global::math.DivReply> DivAsync(global::math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncUnaryCall<global::math.DivReply> DivAsync(global::math.DivArgs request, CallOptions options); + AsyncDuplexStreamingCall<global::math.DivArgs, global::math.DivReply> DivMany(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncDuplexStreamingCall<global::math.DivArgs, global::math.DivReply> DivMany(CallOptions options); + AsyncServerStreamingCall<global::math.Num> Fib(global::math.FibArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncServerStreamingCall<global::math.Num> Fib(global::math.FibArgs request, CallOptions options); + AsyncClientStreamingCall<global::math.Num, global::math.Num> Sum(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncClientStreamingCall<global::math.Num, global::math.Num> Sum(CallOptions options); } // server-side interface @@ -66,30 +75,55 @@ namespace math { public MathClient(Channel channel) : base(channel) { } - public global::math.DivReply Div(global::math.DivArgs request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) + public global::math.DivReply Div(global::math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_Div, headers); - return Calls.BlockingUnaryCall(call, request, cancellationToken); + var call = CreateCall(__Method_Div, new CallOptions(headers, deadline, cancellationToken)); + return Calls.BlockingUnaryCall(call, request); } - public AsyncUnaryCall<global::math.DivReply> DivAsync(global::math.DivArgs request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) + public global::math.DivReply Div(global::math.DivArgs request, CallOptions options) { - var call = CreateCall(__ServiceName, __Method_Div, headers); - return Calls.AsyncUnaryCall(call, request, cancellationToken); + var call = CreateCall(__Method_Div, options); + return Calls.BlockingUnaryCall(call, request); } - public AsyncDuplexStreamingCall<global::math.DivArgs, global::math.DivReply> DivMany(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) + public AsyncUnaryCall<global::math.DivReply> DivAsync(global::math.DivArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_DivMany, headers); - return Calls.AsyncDuplexStreamingCall(call, cancellationToken); + var call = CreateCall(__Method_Div, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncUnaryCall(call, request); } - public AsyncServerStreamingCall<global::math.Num> Fib(global::math.FibArgs request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) + public AsyncUnaryCall<global::math.DivReply> DivAsync(global::math.DivArgs request, CallOptions options) { - var call = CreateCall(__ServiceName, __Method_Fib, headers); - return Calls.AsyncServerStreamingCall(call, request, cancellationToken); + var call = CreateCall(__Method_Div, options); + return Calls.AsyncUnaryCall(call, request); } - public AsyncClientStreamingCall<global::math.Num, global::math.Num> Sum(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) + public AsyncDuplexStreamingCall<global::math.DivArgs, global::math.DivReply> DivMany(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_Sum, headers); - return Calls.AsyncClientStreamingCall(call, cancellationToken); + var call = CreateCall(__Method_DivMany, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncDuplexStreamingCall(call); + } + public AsyncDuplexStreamingCall<global::math.DivArgs, global::math.DivReply> DivMany(CallOptions options) + { + var call = CreateCall(__Method_DivMany, options); + return Calls.AsyncDuplexStreamingCall(call); + } + public AsyncServerStreamingCall<global::math.Num> Fib(global::math.FibArgs request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_Fib, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncServerStreamingCall(call, request); + } + public AsyncServerStreamingCall<global::math.Num> Fib(global::math.FibArgs request, CallOptions options) + { + var call = CreateCall(__Method_Fib, options); + return Calls.AsyncServerStreamingCall(call, request); + } + public AsyncClientStreamingCall<global::math.Num, global::math.Num> Sum(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_Sum, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncClientStreamingCall(call); + } + public AsyncClientStreamingCall<global::math.Num, global::math.Num> Sum(CallOptions options) + { + var call = CreateCall(__Method_Sum, options); + return Calls.AsyncClientStreamingCall(call); } } diff --git a/src/csharp/Grpc.Examples/MathServiceImpl.cs b/src/csharp/Grpc.Examples/MathServiceImpl.cs index 3dd0f53a0d..dd26b1d350 100644 --- a/src/csharp/Grpc.Examples/MathServiceImpl.cs +++ b/src/csharp/Grpc.Examples/MathServiceImpl.cs @@ -54,8 +54,13 @@ namespace math { if (request.Limit <= 0) { - // TODO(jtattermusch): support cancellation - throw new NotImplementedException("Not implemented yet"); + // keep streaming the sequence until cancelled. + IEnumerator<Num> fibEnumerator = FibInternal(long.MaxValue).GetEnumerator(); + while (!context.CancellationToken.IsCancellationRequested && fibEnumerator.MoveNext()) + { + await responseStream.WriteAsync(fibEnumerator.Current); + await Task.Delay(100); + } } if (request.Limit > 0) diff --git a/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj b/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj index 72e110302b..c922ddfb9e 100644 --- a/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj +++ b/src/csharp/Grpc.HealthCheck.Tests/Grpc.HealthCheck.Tests.csproj @@ -17,7 +17,6 @@ <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>bin\Debug\</OutputPath> - <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> </PropertyGroup> @@ -25,10 +24,18 @@ <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release\</OutputPath> - <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\ReleaseSigned</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> + </PropertyGroup> <ItemGroup> <Reference Include="Google.ProtocolBuffers"> <HintPath>..\packages\Google.ProtocolBuffers.2.4.1.555\lib\net40\Google.ProtocolBuffers.dll</HintPath> diff --git a/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs index bc14a0a62f..024377e216 100644 --- a/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs +++ b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs @@ -57,11 +57,13 @@ namespace Grpc.HealthCheck.Tests { serviceImpl = new HealthServiceImpl(); - server = new Server(); - server.AddServiceDefinition(Grpc.Health.V1Alpha.Health.BindService(serviceImpl)); - int port = server.AddListeningPort(Host, Server.PickUnusedPort); + server = new Server + { + Services = { Grpc.Health.V1Alpha.Health.BindService(serviceImpl) }, + Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } + }; server.Start(); - channel = new Channel(Host, port); + channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure); client = Grpc.Health.V1Alpha.Health.NewClient(channel); } diff --git a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj index 4ebb6446dd..0b7a7b91c6 100644 --- a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj +++ b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj @@ -18,7 +18,6 @@ <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>bin\Debug\</OutputPath> - <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> </PropertyGroup> @@ -26,10 +25,18 @@ <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release\</OutputPath> - <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\ReleaseSigned</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> + </PropertyGroup> <ItemGroup> <Reference Include="Google.ProtocolBuffers"> <HintPath>..\packages\Google.ProtocolBuffers.2.4.1.555\lib\net40\Google.ProtocolBuffers.dll</HintPath> diff --git a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec index ca35b36805..acdfba42c8 100644 --- a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec +++ b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.nuspec @@ -20,9 +20,9 @@ </dependencies> </metadata> <files> - <file src="bin/Release/Grpc.HealthCheck.dll" target="lib/net45" /> - <file src="bin/Release/Grpc.HealthCheck.pdb" target="lib/net45" /> - <file src="bin/Release/Grpc.HealthCheck.xml" target="lib/net45" /> + <file src="bin/ReleaseSigned/Grpc.HealthCheck.dll" target="lib/net45" /> + <file src="bin/ReleaseSigned/Grpc.HealthCheck.pdb" target="lib/net45" /> + <file src="bin/ReleaseSigned/Grpc.HealthCheck.xml" target="lib/net45" /> <file src="**\*.cs" target="src" /> </files> </package> diff --git a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs index 217127eca7..0dabc91f7c 100644 --- a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs +++ b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs @@ -17,6 +17,7 @@ namespace Grpc.Health.V1Alpha { static readonly Method<global::Grpc.Health.V1Alpha.HealthCheckRequest, global::Grpc.Health.V1Alpha.HealthCheckResponse> __Method_Check = new Method<global::Grpc.Health.V1Alpha.HealthCheckRequest, global::Grpc.Health.V1Alpha.HealthCheckResponse>( MethodType.Unary, + __ServiceName, "Check", __Marshaller_HealthCheckRequest, __Marshaller_HealthCheckResponse); @@ -24,8 +25,10 @@ namespace Grpc.Health.V1Alpha { // client interface public interface IHealthClient { - global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); - AsyncUnaryCall<global::Grpc.Health.V1Alpha.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); + global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, CallOptions options); + AsyncUnaryCall<global::Grpc.Health.V1Alpha.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncUnaryCall<global::Grpc.Health.V1Alpha.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, CallOptions options); } // server-side interface @@ -40,15 +43,25 @@ namespace Grpc.Health.V1Alpha { public HealthClient(Channel channel) : base(channel) { } - public global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) + public global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_Check, headers); - return Calls.BlockingUnaryCall(call, request, cancellationToken); + var call = CreateCall(__Method_Check, new CallOptions(headers, deadline, cancellationToken)); + return Calls.BlockingUnaryCall(call, request); } - public AsyncUnaryCall<global::Grpc.Health.V1Alpha.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) + public global::Grpc.Health.V1Alpha.HealthCheckResponse Check(global::Grpc.Health.V1Alpha.HealthCheckRequest request, CallOptions options) { - var call = CreateCall(__ServiceName, __Method_Check, headers); - return Calls.AsyncUnaryCall(call, request, cancellationToken); + var call = CreateCall(__Method_Check, options); + return Calls.BlockingUnaryCall(call, request); + } + public AsyncUnaryCall<global::Grpc.Health.V1Alpha.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_Check, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncUnaryCall(call, request); + } + public AsyncUnaryCall<global::Grpc.Health.V1Alpha.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1Alpha.HealthCheckRequest request, CallOptions options) + { + var call = CreateCall(__Method_Check, options); + return Calls.AsyncUnaryCall(call, request); } } diff --git a/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj index dc1d0a44c0..2c38c9645c 100644 --- a/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj +++ b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj @@ -2,7 +2,7 @@ <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> - <Platform Condition=" '$(Platform)' == '' ">x86</Platform> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <ProjectGuid>{3D166931-BA2D-416E-95A3-D36E8F6E90B9}</ProjectGuid> <OutputType>Exe</OutputType> <RootNamespace>Grpc.IntegrationTesting.Client</RootNamespace> @@ -10,7 +10,7 @@ <StartupObject>Grpc.IntegrationTesting.Client.Program</StartupObject> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> @@ -18,17 +18,24 @@ <DefineConstants>DEBUG;</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <Externalconsole>true</Externalconsole> - <PlatformTarget>x86</PlatformTarget> + <PlatformTarget>AnyCPU</PlatformTarget> </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> - <DebugType>full</DebugType> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <Externalconsole>true</Externalconsole> - <PlatformTarget>x86</PlatformTarget> + <PlatformTarget>AnyCPU</PlatformTarget> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\ReleaseSigned</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> </PropertyGroup> <ItemGroup> <Reference Include="System" /> diff --git a/src/csharp/Grpc.IntegrationTesting.Client/app.config b/src/csharp/Grpc.IntegrationTesting.Client/app.config index 966b777192..0a82bb4f16 100644 --- a/src/csharp/Grpc.IntegrationTesting.Client/app.config +++ b/src/csharp/Grpc.IntegrationTesting.Client/app.config @@ -4,7 +4,7 @@ <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.2.28.0" /> + <bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> diff --git a/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj index f03c8f3ce3..949ad61375 100644 --- a/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj +++ b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj @@ -2,7 +2,7 @@ <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> - <Platform Condition=" '$(Platform)' == '' ">x86</Platform> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <ProjectGuid>{A654F3B8-E859-4E6A-B30D-227527DBEF0D}</ProjectGuid> <OutputType>Exe</OutputType> <RootNamespace>Grpc.IntegrationTesting.Server</RootNamespace> @@ -10,7 +10,7 @@ <StartupObject>Grpc.IntegrationTesting.Server.Program</StartupObject> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> @@ -18,17 +18,24 @@ <DefineConstants>DEBUG;</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <Externalconsole>true</Externalconsole> - <PlatformTarget>x86</PlatformTarget> + <PlatformTarget>AnyCPU</PlatformTarget> </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> - <DebugType>full</DebugType> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <Externalconsole>true</Externalconsole> - <PlatformTarget>x86</PlatformTarget> + <PlatformTarget>AnyCPU</PlatformTarget> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\ReleaseSigned</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> </PropertyGroup> <ItemGroup> <Reference Include="System" /> diff --git a/src/csharp/Grpc.IntegrationTesting.Server/app.config b/src/csharp/Grpc.IntegrationTesting.Server/app.config index 966b777192..0a82bb4f16 100644 --- a/src/csharp/Grpc.IntegrationTesting.Server/app.config +++ b/src/csharp/Grpc.IntegrationTesting.Server/app.config @@ -4,7 +4,7 @@ <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.2.28.0" /> + <bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj index d3c69ab9eb..06a75a3351 100644 --- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj +++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj @@ -2,16 +2,15 @@ <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> - <Platform Condition=" '$(Platform)' == '' ">x86</Platform> - <ProductVersion>8.0.30703</ProductVersion> - <SchemaVersion>2.0</SchemaVersion> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <ProjectGuid>{C61154BA-DD4A-4838-8420-0162A28925E0}</ProjectGuid> <OutputType>Library</OutputType> <RootNamespace>Grpc.IntegrationTesting</RootNamespace> <AssemblyName>Grpc.IntegrationTesting</AssemblyName> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + <NuGetPackageImportStamp>041c163e</NuGetPackageImportStamp> </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> @@ -19,46 +18,44 @@ <DefineConstants>DEBUG;</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <Externalconsole>true</Externalconsole> - <PlatformTarget>x86</PlatformTarget> + <PlatformTarget>AnyCPU</PlatformTarget> </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> - <DebugType>full</DebugType> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <Externalconsole>true</Externalconsole> - <PlatformTarget>x86</PlatformTarget> + <PlatformTarget>AnyCPU</PlatformTarget> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\ReleaseSigned</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <SignAssembly>True</SignAssembly> + <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile> </PropertyGroup> <ItemGroup> - <Reference Include="Google.Apis.Auth, Version=1.9.1.12395, Culture=neutral, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\Google.Apis.Auth.1.9.1\lib\net40\Google.Apis.Auth.dll</HintPath> + <Reference Include="BouncyCastle.Crypto"> + <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath> </Reference> - <Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.1.12399, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="Google.Apis.Auth, Version=1.9.2.27817, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\Google.Apis.Auth.1.9.1\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath> + <HintPath>..\packages\Google.Apis.Auth.1.9.2\lib\net40\Google.Apis.Auth.dll</HintPath> </Reference> - <Reference Include="Google.Apis.Core, Version=1.9.1.12394, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.2.27820, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\Google.Apis.Core.1.9.1\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath> + <HintPath>..\packages\Google.Apis.Auth.1.9.2\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath> </Reference> - <Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <Reference Include="Google.Apis.Core, Version=1.9.2.27816, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath> + <HintPath>..\packages\Google.Apis.Core.1.9.2\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath> </Reference> - <Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, 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, 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=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\Newtonsoft.Json.6.0.6\lib\net45\Newtonsoft.Json.dll</HintPath> + <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath> </Reference> <Reference Include="nunit.framework"> <HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath> @@ -67,24 +64,29 @@ <Reference Include="Google.ProtocolBuffers"> <HintPath>..\packages\Google.ProtocolBuffers.2.4.1.521\lib\net40\Google.ProtocolBuffers.dll</HintPath> </Reference> - <Reference Include="System.Collections.Immutable, Version=1.1.36.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\System.Collections.Immutable.1.1.36\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath> - </Reference> <Reference Include="System.Interactive.Async"> <HintPath>..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll</HintPath> </Reference> <Reference Include="System.Net" /> <Reference Include="System.Net.Http" /> - <Reference Include="System.Net.Http.Extensions, Version=2.2.28.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <Reference Include="System.Net.Http.Extensions, Version=2.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\Microsoft.Net.Http.2.2.28\lib\net45\System.Net.Http.Extensions.dll</HintPath> + <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath> </Reference> - <Reference Include="System.Net.Http.Primitives, Version=4.2.28.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <Reference Include="System.Net.Http.Primitives, Version=4.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\Microsoft.Net.Http.2.2.28\lib\net45\System.Net.Http.Primitives.dll</HintPath> + <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"> @@ -99,6 +101,7 @@ <Compile Include="InteropClient.cs" /> <Compile Include="TestCredentials.cs" /> <Compile Include="TestGrpc.cs" /> + <Compile Include="SslCredentialsTest.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup> @@ -133,9 +136,11 @@ <ItemGroup> <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> </ItemGroup> - <Import Project="..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" /> - <Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''"> - <Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" /> - <Error Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" /> + <Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" /> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" /> </Target> </Project>
\ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs index ce255f9423..7411d91d5a 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs @@ -127,15 +127,15 @@ namespace Grpc.IntegrationTesting { credential = credential.CreateScoped(new[] { AuthScope }); } - client.HeaderInterceptor = OAuth2InterceptorFactory.Create(credential); + client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential); } - RunTestCase(options.testCase, client); + RunTestCaseAsync(options.testCase, client).Wait(); } GrpcEnvironment.Shutdown(); } - private void RunTestCase(string testCase, TestService.TestServiceClient client) + private async Task RunTestCaseAsync(string testCase, TestService.TestServiceClient client) { switch (testCase) { @@ -146,16 +146,16 @@ namespace Grpc.IntegrationTesting RunLargeUnary(client); break; case "client_streaming": - RunClientStreaming(client); + await RunClientStreamingAsync(client); break; case "server_streaming": - RunServerStreaming(client); + await RunServerStreamingAsync(client); break; case "ping_pong": - RunPingPong(client); + await RunPingPongAsync(client); break; case "empty_stream": - RunEmptyStream(client); + await RunEmptyStreamAsync(client); break; case "service_account_creds": RunServiceAccountCreds(client); @@ -170,10 +170,10 @@ namespace Grpc.IntegrationTesting RunPerRpcCreds(client); break; case "cancel_after_begin": - RunCancelAfterBegin(client); + await RunCancelAfterBeginAsync(client); break; case "cancel_after_first_response": - RunCancelAfterFirstResponse(client); + await RunCancelAfterFirstResponseAsync(client); break; case "benchmark_empty_unary": RunBenchmarkEmptyUnary(client); @@ -207,118 +207,106 @@ namespace Grpc.IntegrationTesting Console.WriteLine("Passed!"); } - public static void RunClientStreaming(TestService.ITestServiceClient client) + public static async Task RunClientStreamingAsync(TestService.ITestServiceClient client) { - Task.Run(async () => - { - Console.WriteLine("running client_streaming"); + Console.WriteLine("running client_streaming"); - var bodySizes = new List<int> { 27182, 8, 1828, 45904 }.ConvertAll((size) => StreamingInputCallRequest.CreateBuilder().SetPayload(CreateZerosPayload(size)).Build()); + var bodySizes = new List<int> { 27182, 8, 1828, 45904 }.ConvertAll((size) => StreamingInputCallRequest.CreateBuilder().SetPayload(CreateZerosPayload(size)).Build()); - using (var call = client.StreamingInputCall()) - { - await call.RequestStream.WriteAll(bodySizes); + using (var call = client.StreamingInputCall()) + { + await call.RequestStream.WriteAll(bodySizes); - var response = await call.ResponseAsync; - Assert.AreEqual(74922, response.AggregatedPayloadSize); - } - Console.WriteLine("Passed!"); - }).Wait(); + var response = await call.ResponseAsync; + Assert.AreEqual(74922, response.AggregatedPayloadSize); + } + Console.WriteLine("Passed!"); } - public static void RunServerStreaming(TestService.ITestServiceClient client) + public static async Task RunServerStreamingAsync(TestService.ITestServiceClient client) { - Task.Run(async () => - { - Console.WriteLine("running server_streaming"); + Console.WriteLine("running server_streaming"); - var bodySizes = new List<int> { 31415, 9, 2653, 58979 }; + var bodySizes = new List<int> { 31415, 9, 2653, 58979 }; - var request = StreamingOutputCallRequest.CreateBuilder() - .SetResponseType(PayloadType.COMPRESSABLE) - .AddRangeResponseParameters(bodySizes.ConvertAll( - (size) => ResponseParameters.CreateBuilder().SetSize(size).Build())) - .Build(); + var request = StreamingOutputCallRequest.CreateBuilder() + .SetResponseType(PayloadType.COMPRESSABLE) + .AddRangeResponseParameters(bodySizes.ConvertAll( + (size) => ResponseParameters.CreateBuilder().SetSize(size).Build())) + .Build(); - using (var call = client.StreamingOutputCall(request)) + using (var call = client.StreamingOutputCall(request)) + { + var responseList = await call.ResponseStream.ToList(); + foreach (var res in responseList) { - var responseList = await call.ResponseStream.ToList(); - foreach (var res in responseList) - { - Assert.AreEqual(PayloadType.COMPRESSABLE, res.Payload.Type); - } - CollectionAssert.AreEqual(bodySizes, responseList.ConvertAll((item) => item.Payload.Body.Length)); + Assert.AreEqual(PayloadType.COMPRESSABLE, res.Payload.Type); } - Console.WriteLine("Passed!"); - }).Wait(); + CollectionAssert.AreEqual(bodySizes, responseList.ConvertAll((item) => item.Payload.Body.Length)); + } + Console.WriteLine("Passed!"); } - public static void RunPingPong(TestService.ITestServiceClient client) + public static async Task RunPingPongAsync(TestService.ITestServiceClient client) { - Task.Run(async () => - { - Console.WriteLine("running ping_pong"); + Console.WriteLine("running ping_pong"); - using (var call = client.FullDuplexCall()) - { - await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder() - .SetResponseType(PayloadType.COMPRESSABLE) - .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(31415)) - .SetPayload(CreateZerosPayload(27182)).Build()); + using (var call = client.FullDuplexCall()) + { + await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder() + .SetResponseType(PayloadType.COMPRESSABLE) + .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(31415)) + .SetPayload(CreateZerosPayload(27182)).Build()); - Assert.IsTrue(await call.ResponseStream.MoveNext()); - Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type); - Assert.AreEqual(31415, call.ResponseStream.Current.Payload.Body.Length); + Assert.IsTrue(await call.ResponseStream.MoveNext()); + Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type); + Assert.AreEqual(31415, call.ResponseStream.Current.Payload.Body.Length); - await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder() - .SetResponseType(PayloadType.COMPRESSABLE) - .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(9)) - .SetPayload(CreateZerosPayload(8)).Build()); + await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder() + .SetResponseType(PayloadType.COMPRESSABLE) + .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(9)) + .SetPayload(CreateZerosPayload(8)).Build()); - Assert.IsTrue(await call.ResponseStream.MoveNext()); - Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type); - Assert.AreEqual(9, call.ResponseStream.Current.Payload.Body.Length); + Assert.IsTrue(await call.ResponseStream.MoveNext()); + Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type); + Assert.AreEqual(9, call.ResponseStream.Current.Payload.Body.Length); - await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder() - .SetResponseType(PayloadType.COMPRESSABLE) - .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(2653)) - .SetPayload(CreateZerosPayload(1828)).Build()); + await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder() + .SetResponseType(PayloadType.COMPRESSABLE) + .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(2653)) + .SetPayload(CreateZerosPayload(1828)).Build()); - Assert.IsTrue(await call.ResponseStream.MoveNext()); - Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type); - Assert.AreEqual(2653, call.ResponseStream.Current.Payload.Body.Length); + Assert.IsTrue(await call.ResponseStream.MoveNext()); + Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type); + Assert.AreEqual(2653, call.ResponseStream.Current.Payload.Body.Length); - await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder() - .SetResponseType(PayloadType.COMPRESSABLE) - .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(58979)) - .SetPayload(CreateZerosPayload(45904)).Build()); + await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder() + .SetResponseType(PayloadType.COMPRESSABLE) + .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(58979)) + .SetPayload(CreateZerosPayload(45904)).Build()); - Assert.IsTrue(await call.ResponseStream.MoveNext()); - Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type); - Assert.AreEqual(58979, call.ResponseStream.Current.Payload.Body.Length); + Assert.IsTrue(await call.ResponseStream.MoveNext()); + Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type); + Assert.AreEqual(58979, call.ResponseStream.Current.Payload.Body.Length); - await call.RequestStream.CompleteAsync(); + await call.RequestStream.CompleteAsync(); - Assert.IsFalse(await call.ResponseStream.MoveNext()); - } - Console.WriteLine("Passed!"); - }).Wait(); + Assert.IsFalse(await call.ResponseStream.MoveNext()); + } + Console.WriteLine("Passed!"); } - public static void RunEmptyStream(TestService.ITestServiceClient client) + public static async Task RunEmptyStreamAsync(TestService.ITestServiceClient client) { - Task.Run(async () => + Console.WriteLine("running empty_stream"); + using (var call = client.FullDuplexCall()) { - Console.WriteLine("running empty_stream"); - using (var call = client.FullDuplexCall()) - { - await call.RequestStream.CompleteAsync(); + await call.RequestStream.CompleteAsync(); - var responseList = await call.ResponseStream.ToList(); - Assert.AreEqual(0, responseList.Count); - } - Console.WriteLine("Passed!"); - }).Wait(); + var responseList = await call.ResponseStream.ToList(); + Assert.AreEqual(0, responseList.Count); + } + Console.WriteLine("Passed!"); } public static void RunServiceAccountCreds(TestService.ITestServiceClient client) @@ -368,11 +356,7 @@ namespace Grpc.IntegrationTesting Assert.IsTrue(credential.RequestAccessTokenAsync(CancellationToken.None).Result); string oauth2Token = credential.Token.AccessToken; - // Intercept calls with an OAuth2 token obtained out-of-band. - client.HeaderInterceptor = new MetadataInterceptorDelegate((metadata) => - { - metadata.Add(new Metadata.Entry("Authorization", "Bearer " + oauth2Token)); - }); + client.HeaderInterceptor = OAuth2Interceptors.FromAccessToken(oauth2Token); var request = SimpleRequest.CreateBuilder() .SetFillUsername(true) @@ -393,78 +377,75 @@ namespace Grpc.IntegrationTesting var credential = GoogleCredential.GetApplicationDefault().CreateScoped(new[] { AuthScope }); Assert.IsTrue(credential.RequestAccessTokenAsync(CancellationToken.None).Result); string oauth2Token = credential.Token.AccessToken; + var headerInterceptor = OAuth2Interceptors.FromAccessToken(oauth2Token); var request = SimpleRequest.CreateBuilder() .SetFillUsername(true) .SetFillOauthScope(true) .Build(); - var response = client.UnaryCall(request, headers: new Metadata { new Metadata.Entry("Authorization", "Bearer " + oauth2Token) }); + var headers = new Metadata(); + headerInterceptor(headers); + var response = client.UnaryCall(request, headers: headers); Assert.AreEqual(AuthScopeResponse, response.OauthScope); Assert.AreEqual(ServiceAccountUser, response.Username); Console.WriteLine("Passed!"); } - public static void RunCancelAfterBegin(TestService.ITestServiceClient client) + public static async Task RunCancelAfterBeginAsync(TestService.ITestServiceClient client) { - Task.Run(async () => + Console.WriteLine("running cancel_after_begin"); + + var cts = new CancellationTokenSource(); + using (var call = client.StreamingInputCall(cancellationToken: cts.Token)) { - Console.WriteLine("running cancel_after_begin"); + // TODO(jtattermusch): we need this to ensure call has been initiated once we cancel it. + await Task.Delay(1000); + cts.Cancel(); - var cts = new CancellationTokenSource(); - using (var call = client.StreamingInputCall(cancellationToken: cts.Token)) + try { - // TODO(jtattermusch): we need this to ensure call has been initiated once we cancel it. - await Task.Delay(1000); - cts.Cancel(); - - try - { - var response = await call.ResponseAsync; - Assert.Fail(); - } - catch (RpcException e) - { - Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode); - } + var response = await call.ResponseAsync; + Assert.Fail(); + } + catch (RpcException e) + { + Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode); } - Console.WriteLine("Passed!"); - }).Wait(); + } + Console.WriteLine("Passed!"); } - public static void RunCancelAfterFirstResponse(TestService.ITestServiceClient client) + public static async Task RunCancelAfterFirstResponseAsync(TestService.ITestServiceClient client) { - Task.Run(async () => - { - Console.WriteLine("running cancel_after_first_response"); + Console.WriteLine("running cancel_after_first_response"); - var cts = new CancellationTokenSource(); - using (var call = client.FullDuplexCall(cancellationToken: cts.Token)) - { - await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder() - .SetResponseType(PayloadType.COMPRESSABLE) - .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(31415)) - .SetPayload(CreateZerosPayload(27182)).Build()); + var cts = new CancellationTokenSource(); + using (var call = client.FullDuplexCall(cancellationToken: cts.Token)) + { + await call.RequestStream.WriteAsync(StreamingOutputCallRequest.CreateBuilder() + .SetResponseType(PayloadType.COMPRESSABLE) + .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(31415)) + .SetPayload(CreateZerosPayload(27182)).Build()); - Assert.IsTrue(await call.ResponseStream.MoveNext()); - Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type); - Assert.AreEqual(31415, call.ResponseStream.Current.Payload.Body.Length); + Assert.IsTrue(await call.ResponseStream.MoveNext()); + Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Current.Payload.Type); + Assert.AreEqual(31415, call.ResponseStream.Current.Payload.Body.Length); - cts.Cancel(); + cts.Cancel(); - try - { - await call.ResponseStream.MoveNext(); - Assert.Fail(); - } - catch (RpcException e) - { - Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode); - } + try + { + await call.ResponseStream.MoveNext(); + Assert.Fail(); } - Console.WriteLine("Passed!"); - }).Wait(); + catch (RpcException e) + { + Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode); + } + } + Console.WriteLine("Passed!"); } // This is not an official interop test, but it's useful. diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs index f306289cfb..6fa721bc1c 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs @@ -33,6 +33,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using grpc.testing; @@ -47,7 +48,7 @@ namespace Grpc.IntegrationTesting /// </summary> public class InteropClientServerTest { - string host = "localhost"; + const string Host = "localhost"; Server server; Channel channel; TestService.ITestServiceClient client; @@ -55,16 +56,19 @@ namespace Grpc.IntegrationTesting [TestFixtureSetUp] public void Init() { - server = new Server(); - server.AddServiceDefinition(TestService.BindService(new TestServiceImpl())); - int port = server.AddListeningPort(host, Server.PickUnusedPort, TestCredentials.CreateTestServerCredentials()); + server = new Server + { + Services = { TestService.BindService(new TestServiceImpl()) }, + Ports = { { Host, ServerPort.PickUnused, TestCredentials.CreateTestServerCredentials() } } + }; server.Start(); var options = new List<ChannelOption> { new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride) }; - channel = new Channel(host, port, TestCredentials.CreateTestClientCredentials(true), options); + int port = server.Ports.Single().BoundPort; + channel = new Channel(Host, port, TestCredentials.CreateTestClientCredentials(true), options); client = TestService.NewClient(channel); } @@ -89,39 +93,39 @@ namespace Grpc.IntegrationTesting } [Test] - public void ClientStreaming() + public async Task ClientStreaming() { - InteropClient.RunClientStreaming(client); + await InteropClient.RunClientStreamingAsync(client); } [Test] - public void ServerStreaming() + public async Task ServerStreaming() { - InteropClient.RunServerStreaming(client); + await InteropClient.RunServerStreamingAsync(client); } [Test] - public void PingPong() + public async Task PingPong() { - InteropClient.RunPingPong(client); + await InteropClient.RunPingPongAsync(client); } [Test] - public void EmptyStream() + public async Task EmptyStream() { - InteropClient.RunEmptyStream(client); + await InteropClient.RunEmptyStreamAsync(client); } [Test] - public void CancelAfterBegin() + public async Task CancelAfterBegin() { - InteropClient.RunCancelAfterBegin(client); + await InteropClient.RunCancelAfterBeginAsync(client); } [Test] - public void CancelAfterFirstResponse() + public async Task CancelAfterFirstResponse() { - InteropClient.RunCancelAfterFirstResponse(client); + await InteropClient.RunCancelAfterFirstResponseAsync(client); } } } diff --git a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs index 9475e66c40..504fd11857 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs @@ -88,18 +88,20 @@ namespace Grpc.IntegrationTesting private void Run() { - var server = new Server(); - server.AddServiceDefinition(TestService.BindService(new TestServiceImpl())); + var server = new Server + { + Services = { TestService.BindService(new TestServiceImpl()) } + }; string host = "0.0.0.0"; int port = options.port.Value; if (options.useTls) { - server.AddListeningPort(host, port, TestCredentials.CreateTestServerCredentials()); + server.Ports.Add(host, port, TestCredentials.CreateTestServerCredentials()); } else { - server.AddListeningPort(host, options.port.Value); + server.Ports.Add(host, options.port.Value, ServerCredentials.Insecure); } Console.WriteLine("Running server on " + string.Format("{0}:{1}", host, port)); server.Start(); diff --git a/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs new file mode 100644 index 0000000000..1c398eb84e --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs @@ -0,0 +1,100 @@ +#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.Linq; +using System.Threading; +using System.Threading.Tasks; +using grpc.testing; +using Grpc.Core; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.IntegrationTesting +{ + /// <summary> + /// Test SSL credentials where server authenticates client + /// and client authenticates the server. + /// </summary> + public class SslCredentialsTest + { + const string Host = "localhost"; + Server server; + Channel channel; + TestService.ITestServiceClient client; + + [TestFixtureSetUp] + public void Init() + { + var rootCert = File.ReadAllText(TestCredentials.ClientCertAuthorityPath); + var keyCertPair = new KeyCertificatePair( + File.ReadAllText(TestCredentials.ServerCertChainPath), + File.ReadAllText(TestCredentials.ServerPrivateKeyPath)); + + var serverCredentials = new SslServerCredentials(new[] { keyCertPair }, rootCert, true); + var clientCredentials = new SslCredentials(rootCert, keyCertPair); + + server = new Server + { + Services = { TestService.BindService(new TestServiceImpl()) }, + Ports = { { Host, ServerPort.PickUnused, serverCredentials } } + }; + server.Start(); + + var options = new List<ChannelOption> + { + new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride) + }; + + channel = new Channel(Host, server.Ports.Single().BoundPort, clientCredentials, options); + client = TestService.NewClient(channel); + } + + [TestFixtureTearDown] + public void Cleanup() + { + channel.Dispose(); + server.ShutdownAsync().Wait(); + GrpcEnvironment.Shutdown(); + } + + [Test] + public void AuthenticatedClientAndServer() + { + var response = client.UnaryCall(SimpleRequest.CreateBuilder().SetResponseSize(10).Build()); + Assert.AreEqual(10, response.Payload.Body.Length); + } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting/TestCredentials.cs b/src/csharp/Grpc.IntegrationTesting/TestCredentials.cs index 401c50b1ae..da0b7fb910 100644 --- a/src/csharp/Grpc.IntegrationTesting/TestCredentials.cs +++ b/src/csharp/Grpc.IntegrationTesting/TestCredentials.cs @@ -33,7 +33,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Text.RegularExpressions; @@ -78,7 +77,7 @@ namespace Grpc.IntegrationTesting var keyCertPair = new KeyCertificatePair( File.ReadAllText(ServerCertChainPath), File.ReadAllText(ServerPrivateKeyPath)); - return new SslServerCredentials(ImmutableList.Create(keyCertPair)); + return new SslServerCredentials(new[] { keyCertPair }); } } } diff --git a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs index de2fa07441..697acb53d8 100644 --- a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs +++ b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs @@ -22,36 +22,42 @@ namespace grpc.testing { static readonly Method<global::grpc.testing.Empty, global::grpc.testing.Empty> __Method_EmptyCall = new Method<global::grpc.testing.Empty, global::grpc.testing.Empty>( MethodType.Unary, + __ServiceName, "EmptyCall", __Marshaller_Empty, __Marshaller_Empty); static readonly Method<global::grpc.testing.SimpleRequest, global::grpc.testing.SimpleResponse> __Method_UnaryCall = new Method<global::grpc.testing.SimpleRequest, global::grpc.testing.SimpleResponse>( MethodType.Unary, + __ServiceName, "UnaryCall", __Marshaller_SimpleRequest, __Marshaller_SimpleResponse); static readonly Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> __Method_StreamingOutputCall = new Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse>( MethodType.ServerStreaming, + __ServiceName, "StreamingOutputCall", __Marshaller_StreamingOutputCallRequest, __Marshaller_StreamingOutputCallResponse); static readonly Method<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse> __Method_StreamingInputCall = new Method<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse>( MethodType.ClientStreaming, + __ServiceName, "StreamingInputCall", __Marshaller_StreamingInputCallRequest, __Marshaller_StreamingInputCallResponse); static readonly Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> __Method_FullDuplexCall = new Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse>( MethodType.DuplexStreaming, + __ServiceName, "FullDuplexCall", __Marshaller_StreamingOutputCallRequest, __Marshaller_StreamingOutputCallResponse); static readonly Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> __Method_HalfDuplexCall = new Method<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse>( MethodType.DuplexStreaming, + __ServiceName, "HalfDuplexCall", __Marshaller_StreamingOutputCallRequest, __Marshaller_StreamingOutputCallResponse); @@ -59,14 +65,22 @@ namespace grpc.testing { // client interface public interface ITestServiceClient { - global::grpc.testing.Empty EmptyCall(global::grpc.testing.Empty request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); - AsyncUnaryCall<global::grpc.testing.Empty> EmptyCallAsync(global::grpc.testing.Empty request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); - global::grpc.testing.SimpleResponse UnaryCall(global::grpc.testing.SimpleRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); - AsyncUnaryCall<global::grpc.testing.SimpleResponse> UnaryCallAsync(global::grpc.testing.SimpleRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); - AsyncServerStreamingCall<global::grpc.testing.StreamingOutputCallResponse> StreamingOutputCall(global::grpc.testing.StreamingOutputCallRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); - AsyncClientStreamingCall<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse> StreamingInputCall(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); - AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> FullDuplexCall(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); - AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> HalfDuplexCall(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)); + global::grpc.testing.Empty EmptyCall(global::grpc.testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + global::grpc.testing.Empty EmptyCall(global::grpc.testing.Empty request, CallOptions options); + AsyncUnaryCall<global::grpc.testing.Empty> EmptyCallAsync(global::grpc.testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncUnaryCall<global::grpc.testing.Empty> EmptyCallAsync(global::grpc.testing.Empty request, CallOptions options); + global::grpc.testing.SimpleResponse UnaryCall(global::grpc.testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + global::grpc.testing.SimpleResponse UnaryCall(global::grpc.testing.SimpleRequest request, CallOptions options); + AsyncUnaryCall<global::grpc.testing.SimpleResponse> UnaryCallAsync(global::grpc.testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncUnaryCall<global::grpc.testing.SimpleResponse> UnaryCallAsync(global::grpc.testing.SimpleRequest request, CallOptions options); + AsyncServerStreamingCall<global::grpc.testing.StreamingOutputCallResponse> StreamingOutputCall(global::grpc.testing.StreamingOutputCallRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncServerStreamingCall<global::grpc.testing.StreamingOutputCallResponse> StreamingOutputCall(global::grpc.testing.StreamingOutputCallRequest request, CallOptions options); + AsyncClientStreamingCall<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse> StreamingInputCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncClientStreamingCall<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse> StreamingInputCall(CallOptions options); + AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> FullDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> FullDuplexCall(CallOptions options); + AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> HalfDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)); + AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> HalfDuplexCall(CallOptions options); } // server-side interface @@ -86,45 +100,85 @@ namespace grpc.testing { public TestServiceClient(Channel channel) : base(channel) { } - public global::grpc.testing.Empty EmptyCall(global::grpc.testing.Empty request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) + public global::grpc.testing.Empty EmptyCall(global::grpc.testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_EmptyCall, headers); - return Calls.BlockingUnaryCall(call, request, cancellationToken); + var call = CreateCall(__Method_EmptyCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.BlockingUnaryCall(call, request); } - public AsyncUnaryCall<global::grpc.testing.Empty> EmptyCallAsync(global::grpc.testing.Empty request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) + public global::grpc.testing.Empty EmptyCall(global::grpc.testing.Empty request, CallOptions options) { - var call = CreateCall(__ServiceName, __Method_EmptyCall, headers); - return Calls.AsyncUnaryCall(call, request, cancellationToken); + var call = CreateCall(__Method_EmptyCall, options); + return Calls.BlockingUnaryCall(call, request); } - public global::grpc.testing.SimpleResponse UnaryCall(global::grpc.testing.SimpleRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) + public AsyncUnaryCall<global::grpc.testing.Empty> EmptyCallAsync(global::grpc.testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_UnaryCall, headers); - return Calls.BlockingUnaryCall(call, request, cancellationToken); + var call = CreateCall(__Method_EmptyCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncUnaryCall(call, request); } - public AsyncUnaryCall<global::grpc.testing.SimpleResponse> UnaryCallAsync(global::grpc.testing.SimpleRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) + public AsyncUnaryCall<global::grpc.testing.Empty> EmptyCallAsync(global::grpc.testing.Empty request, CallOptions options) { - var call = CreateCall(__ServiceName, __Method_UnaryCall, headers); - return Calls.AsyncUnaryCall(call, request, cancellationToken); + var call = CreateCall(__Method_EmptyCall, options); + return Calls.AsyncUnaryCall(call, request); } - public AsyncServerStreamingCall<global::grpc.testing.StreamingOutputCallResponse> StreamingOutputCall(global::grpc.testing.StreamingOutputCallRequest request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) + public global::grpc.testing.SimpleResponse UnaryCall(global::grpc.testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_StreamingOutputCall, headers); - return Calls.AsyncServerStreamingCall(call, request, cancellationToken); + var call = CreateCall(__Method_UnaryCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.BlockingUnaryCall(call, request); } - public AsyncClientStreamingCall<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse> StreamingInputCall(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) + public global::grpc.testing.SimpleResponse UnaryCall(global::grpc.testing.SimpleRequest request, CallOptions options) { - var call = CreateCall(__ServiceName, __Method_StreamingInputCall, headers); - return Calls.AsyncClientStreamingCall(call, cancellationToken); + var call = CreateCall(__Method_UnaryCall, options); + return Calls.BlockingUnaryCall(call, request); } - public AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> FullDuplexCall(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) + public AsyncUnaryCall<global::grpc.testing.SimpleResponse> UnaryCallAsync(global::grpc.testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) { - var call = CreateCall(__ServiceName, __Method_FullDuplexCall, headers); - return Calls.AsyncDuplexStreamingCall(call, cancellationToken); + var call = CreateCall(__Method_UnaryCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncUnaryCall(call, request); } - public AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> HalfDuplexCall(Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken)) + public AsyncUnaryCall<global::grpc.testing.SimpleResponse> UnaryCallAsync(global::grpc.testing.SimpleRequest request, CallOptions options) { - var call = CreateCall(__ServiceName, __Method_HalfDuplexCall, headers); - return Calls.AsyncDuplexStreamingCall(call, cancellationToken); + var call = CreateCall(__Method_UnaryCall, options); + return Calls.AsyncUnaryCall(call, request); + } + public AsyncServerStreamingCall<global::grpc.testing.StreamingOutputCallResponse> StreamingOutputCall(global::grpc.testing.StreamingOutputCallRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_StreamingOutputCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncServerStreamingCall(call, request); + } + public AsyncServerStreamingCall<global::grpc.testing.StreamingOutputCallResponse> StreamingOutputCall(global::grpc.testing.StreamingOutputCallRequest request, CallOptions options) + { + var call = CreateCall(__Method_StreamingOutputCall, options); + return Calls.AsyncServerStreamingCall(call, request); + } + public AsyncClientStreamingCall<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse> StreamingInputCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_StreamingInputCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncClientStreamingCall(call); + } + public AsyncClientStreamingCall<global::grpc.testing.StreamingInputCallRequest, global::grpc.testing.StreamingInputCallResponse> StreamingInputCall(CallOptions options) + { + var call = CreateCall(__Method_StreamingInputCall, options); + return Calls.AsyncClientStreamingCall(call); + } + public AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> FullDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_FullDuplexCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncDuplexStreamingCall(call); + } + public AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> FullDuplexCall(CallOptions options) + { + var call = CreateCall(__Method_FullDuplexCall, options); + return Calls.AsyncDuplexStreamingCall(call); + } + public AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> HalfDuplexCall(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var call = CreateCall(__Method_HalfDuplexCall, new CallOptions(headers, deadline, cancellationToken)); + return Calls.AsyncDuplexStreamingCall(call); + } + public AsyncDuplexStreamingCall<global::grpc.testing.StreamingOutputCallRequest, global::grpc.testing.StreamingOutputCallResponse> HalfDuplexCall(CallOptions options) + { + var call = CreateCall(__Method_HalfDuplexCall, options); + return Calls.AsyncDuplexStreamingCall(call); } } diff --git a/src/csharp/Grpc.IntegrationTesting/app.config b/src/csharp/Grpc.IntegrationTesting/app.config index 966b777192..0a82bb4f16 100644 --- a/src/csharp/Grpc.IntegrationTesting/app.config +++ b/src/csharp/Grpc.IntegrationTesting/app.config @@ -4,7 +4,7 @@ <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-4.2.28.0" newVersion="4.2.28.0" /> + <bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> diff --git a/src/csharp/Grpc.IntegrationTesting/packages.config b/src/csharp/Grpc.IntegrationTesting/packages.config index c74ac75d79..7d1f84f303 100644 --- a/src/csharp/Grpc.IntegrationTesting/packages.config +++ b/src/csharp/Grpc.IntegrationTesting/packages.config @@ -1,14 +1,14 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="Google.Apis.Auth" version="1.9.1" targetFramework="net45" /> - <package id="Google.Apis.Core" version="1.9.1" targetFramework="net45" /> + <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.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.9" 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.14" targetFramework="net45" /> - <package id="Microsoft.Net.Http" version="2.2.28" targetFramework="net45" /> - <package id="Newtonsoft.Json" version="6.0.6" targetFramework="net45" /> + <package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" /> + <package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" /> + <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" /> <package id="NUnit" version="2.6.4" targetFramework="net45" /> - <package id="System.Collections.Immutable" version="1.1.36" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/src/csharp/Grpc.sln b/src/csharp/Grpc.sln index 705e4fb1c2..f19f29c6a2 100644 --- a/src/csharp/Grpc.sln +++ b/src/csharp/Grpc.sln @@ -34,60 +34,83 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.HealthCheck.Tests", "G EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|x86 = Debug|x86
- Release|x86 = Release|x86
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ ReleaseSigned|Any CPU = ReleaseSigned|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Debug|x86.ActiveCfg = Debug|Any CPU
- {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Debug|x86.Build.0 = Debug|Any CPU
- {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|x86.ActiveCfg = Release|Any CPU
- {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|x86.Build.0 = Release|Any CPU
- {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|x86.ActiveCfg = Debug|x86
- {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|x86.Build.0 = Debug|x86
- {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|x86.ActiveCfg = Release|x86
- {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|x86.Build.0 = Release|x86
- {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.ActiveCfg = Debug|x86
- {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.Build.0 = Debug|x86
- {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.ActiveCfg = Release|x86
- {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.Build.0 = Release|x86
- {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.ActiveCfg = Debug|Any CPU
- {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.Build.0 = Debug|Any CPU
- {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.ActiveCfg = Release|Any CPU
- {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.Build.0 = Release|Any CPU
- {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.ActiveCfg = Debug|Any CPU
- {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.Build.0 = Debug|Any CPU
- {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.ActiveCfg = Release|Any CPU
- {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.Build.0 = Release|Any CPU
- {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|x86.ActiveCfg = Debug|x86
- {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|x86.Build.0 = Debug|x86
- {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|x86.ActiveCfg = Release|x86
- {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|x86.Build.0 = Release|x86
- {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Debug|x86.ActiveCfg = Debug|Any CPU
- {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Debug|x86.Build.0 = Debug|Any CPU
- {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Release|x86.ActiveCfg = Release|Any CPU
- {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Release|x86.Build.0 = Release|Any CPU
- {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Debug|x86.ActiveCfg = Debug|Any CPU
- {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Debug|x86.Build.0 = Debug|Any CPU
- {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Release|x86.ActiveCfg = Release|Any CPU
- {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Release|x86.Build.0 = Release|Any CPU
- {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|x86.ActiveCfg = Debug|x86
- {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|x86.Build.0 = Debug|x86
- {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|x86.ActiveCfg = Release|x86
- {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|x86.Build.0 = Release|x86
- {C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|x86.ActiveCfg = Debug|x86
- {C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|x86.Build.0 = Debug|x86
- {C61154BA-DD4A-4838-8420-0162A28925E0}.Release|x86.ActiveCfg = Release|x86
- {C61154BA-DD4A-4838-8420-0162A28925E0}.Release|x86.Build.0 = Release|x86
- {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.ActiveCfg = Debug|Any CPU
- {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.Build.0 = Debug|Any CPU
- {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.ActiveCfg = Release|Any CPU
- {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.Build.0 = Release|Any CPU
- {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Debug|x86.ActiveCfg = Debug|Any CPU
- {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Debug|x86.Build.0 = Debug|Any CPU
- {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Release|x86.ActiveCfg = Release|Any CPU
- {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Release|x86.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
+ {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
+ {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
+ {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
+ {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
+ {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
+ {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
+ {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
+ {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
+ {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
+ {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
+ {C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C61154BA-DD4A-4838-8420-0162A28925E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C61154BA-DD4A-4838-8420-0162A28925E0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C61154BA-DD4A-4838-8420-0162A28925E0}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
+ {C61154BA-DD4A-4838-8420-0162A28925E0}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
+ {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
+ {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
+ {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
+ {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
+ {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BF62FE08-373A-43D6-9D73-41CAA38B7011}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
+ {BF62FE08-373A-43D6-9D73-41CAA38B7011}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
+ {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
+ {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
+ {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
+ {AA5E328A-8835-49D7-98ED-C29F2B3049F0}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
+ {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
+ {F8C6D937-C44B-4EE3-A431-B0FBAEACE47D}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/csharp/build_packages.bat b/src/csharp/build_packages.bat index c3e5fe8817..9e1253bf0b 100644 --- a/src/csharp/build_packages.bat +++ b/src/csharp/build_packages.bat @@ -12,7 +12,7 @@ cd ..\..\vsprojects\nuget_package @call buildall.bat || goto :error endlocal -@call buildall.bat || goto :error +@call buildall.bat BUILD_SIGNED || goto :error %NUGET% pack ..\..\vsprojects\nuget_package\grpc.native.csharp_ext.nuspec -Version %CORE_VERSION% || goto :error %NUGET% pack Grpc.Auth\Grpc.Auth.nuspec -Symbols -Version %VERSION% || goto :error diff --git a/src/csharp/buildall.bat b/src/csharp/buildall.bat index 16860aec3c..d85896c255 100644 --- a/src/csharp/buildall.bat +++ b/src/csharp/buildall.bat @@ -9,10 +9,15 @@ cd /d %~dp0 @call "%VS120COMNTOOLS%\..\..\vc\vcvarsall.bat" x86 @rem Build the C# native extension -msbuild ..\..\vsprojects\grpc.sln /t:grpc_csharp_ext /p:PlatformToolset=v120 || goto :error +msbuild ..\..\vsprojects\grpc_csharp_ext.sln /p:PlatformToolset=v120 || goto :error msbuild Grpc.sln /p:Configuration=Debug || goto :error msbuild Grpc.sln /p:Configuration=Release || goto :error + +if "%1" == "BUILD_SIGNED" ( +msbuild Grpc.sln /p:Configuration=ReleaseSigned || goto :error +) + endlocal goto :EOF diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index 682521446f..048887bc12 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -169,7 +169,7 @@ grpcsharp_metadata_array_add(grpc_metadata_array *array, const char *key, GPR_EXPORT gpr_intptr GPR_CALLTYPE grpcsharp_metadata_array_count(grpc_metadata_array *array) { - return (gpr_intptr) array->count; + return (gpr_intptr)array->count; } GPR_EXPORT const char *GPR_CALLTYPE @@ -184,10 +184,10 @@ grpcsharp_metadata_array_get_value(grpc_metadata_array *array, size_t index) { return array->metadata[index].value; } -GPR_EXPORT gpr_intptr GPR_CALLTYPE -grpcsharp_metadata_array_get_value_length(grpc_metadata_array *array, size_t index) { +GPR_EXPORT gpr_intptr GPR_CALLTYPE grpcsharp_metadata_array_get_value_length( + grpc_metadata_array *array, size_t index) { GPR_ASSERT(index < array->count); - return (gpr_intptr) array->metadata[index].value_length; + return (gpr_intptr)array->metadata[index].value_length; } /* Move contents of metadata array */ @@ -306,8 +306,7 @@ grpcsharp_batch_context_server_rpc_new_method( return ctx->server_rpc_new.call_details.method; } -GPR_EXPORT const char *GPR_CALLTYPE -grpcsharp_batch_context_server_rpc_new_host( +GPR_EXPORT const char *GPR_CALLTYPE grpcsharp_batch_context_server_rpc_new_host( const grpcsharp_batch_context *ctx) { return ctx->server_rpc_new.call_details.host; } @@ -367,8 +366,9 @@ grpcsharp_completion_queue_pluck(grpc_completion_queue *cq, void *tag) { /* Channel */ GPR_EXPORT grpc_channel *GPR_CALLTYPE -grpcsharp_channel_create(const char *target, const grpc_channel_args *args) { - return grpc_channel_create(target, args); + +grpcsharp_insecure_channel_create(const char *target, const grpc_channel_args *args) { + return grpc_insecure_channel_create(target, args); } GPR_EXPORT void GPR_CALLTYPE grpcsharp_channel_destroy(grpc_channel *channel) { @@ -379,7 +379,24 @@ GPR_EXPORT grpc_call *GPR_CALLTYPE grpcsharp_channel_create_call(grpc_channel *channel, grpc_completion_queue *cq, const char *method, const char *host, gpr_timespec deadline) { - return grpc_channel_create_call(channel, cq, method, host, deadline); + return grpc_channel_create_call(channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq, + method, host, deadline); +} + +GPR_EXPORT grpc_connectivity_state GPR_CALLTYPE +grpcsharp_channel_check_connectivity_state(grpc_channel *channel, gpr_int32 try_to_connect) { + return grpc_channel_check_connectivity_state(channel, try_to_connect); +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_channel_watch_connectivity_state( + grpc_channel *channel, grpc_connectivity_state last_observed_state, + gpr_timespec deadline, grpc_completion_queue *cq, grpcsharp_batch_context *ctx) { + grpc_channel_watch_connectivity_state(channel, last_observed_state, + deadline, cq, ctx); +} + +GPR_EXPORT char *GPR_CALLTYPE grpcsharp_channel_get_target(grpc_channel *channel) { + return grpc_channel_get_target(channel); } /* Channel args */ @@ -433,10 +450,20 @@ grpcsharp_channel_args_destroy(grpc_channel_args *args) { /* Timespec */ -GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_now(void) { return gpr_now(GPR_CLOCK_REALTIME); } +GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_now(gpr_clock_type clock_type) { + return gpr_now(clock_type); +} -GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_inf_future(void) { - return gpr_inf_future(GPR_CLOCK_REALTIME); +GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_inf_future(gpr_clock_type clock_type) { + return gpr_inf_future(clock_type); +} + +GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_inf_past(gpr_clock_type clock_type) { + return gpr_inf_past(clock_type); +} + +GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_convert_clock_type(gpr_timespec t, gpr_clock_type target_clock) { + return gpr_convert_clock_type(t, target_clock); } GPR_EXPORT gpr_int32 GPR_CALLTYPE gprsharp_sizeof_timespec(void) { @@ -455,6 +482,14 @@ grpcsharp_call_cancel_with_status(grpc_call *call, grpc_status_code status, return grpc_call_cancel_with_status(call, status, description); } +GPR_EXPORT char *GPR_CALLTYPE grpcsharp_call_get_peer(grpc_call *call) { + return grpc_call_get_peer(call); +} + +GPR_EXPORT void GPR_CALLTYPE gprsharp_free(void *p) { + gpr_free(p); +} + GPR_EXPORT void GPR_CALLTYPE grpcsharp_call_destroy(grpc_call *call) { grpc_call_destroy(call); } @@ -638,20 +673,17 @@ grpcsharp_call_send_close_from_client(grpc_call *call, return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx); } -GPR_EXPORT grpc_call_error GPR_CALLTYPE -grpcsharp_call_send_status_from_server(grpc_call *call, - grpcsharp_batch_context *ctx, - grpc_status_code status_code, - const char *status_details, - grpc_metadata_array *trailing_metadata) { +GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_status_from_server( + grpc_call *call, grpcsharp_batch_context *ctx, grpc_status_code status_code, + const char *status_details, grpc_metadata_array *trailing_metadata) { /* TODO: don't use magic number */ grpc_op ops[1]; ops[0].op = GRPC_OP_SEND_STATUS_FROM_SERVER; ops[0].data.send_status_from_server.status = status_code; ops[0].data.send_status_from_server.status_details = gpr_strdup(status_details); - grpcsharp_metadata_array_move(&(ctx->send_status_from_server.trailing_metadata), - trailing_metadata); + grpcsharp_metadata_array_move( + &(ctx->send_status_from_server.trailing_metadata), trailing_metadata); ops[0].data.send_status_from_server.trailing_metadata_count = ctx->send_status_from_server.trailing_metadata.count; ops[0].data.send_status_from_server.trailing_metadata = @@ -699,8 +731,8 @@ grpcsharp_server_create(grpc_completion_queue *cq, } GPR_EXPORT gpr_int32 GPR_CALLTYPE -grpcsharp_server_add_http2_port(grpc_server *server, const char *addr) { - return grpc_server_add_http2_port(server, addr); +grpcsharp_server_add_insecure_http2_port(grpc_server *server, const char *addr) { + return grpc_server_add_insecure_http2_port(server, addr); } GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_start(grpc_server *server) { @@ -761,7 +793,8 @@ grpcsharp_secure_channel_create(grpc_credentials *creds, const char *target, GPR_EXPORT grpc_server_credentials *GPR_CALLTYPE grpcsharp_ssl_server_credentials_create( const char *pem_root_certs, const char **key_cert_pair_cert_chain_array, - const char **key_cert_pair_private_key_array, size_t num_key_cert_pairs) { + const char **key_cert_pair_private_key_array, size_t num_key_cert_pairs, + int force_client_auth) { size_t i; grpc_server_credentials *creds; grpc_ssl_pem_key_cert_pair *key_cert_pairs = @@ -777,7 +810,8 @@ grpcsharp_ssl_server_credentials_create( } } creds = grpc_ssl_server_credentials_create(pem_root_certs, key_cert_pairs, - num_key_cert_pairs); + num_key_cert_pairs, + force_client_auth); gpr_free(key_cert_pairs); return creds; } diff --git a/src/csharp/keys/Grpc.public.snk b/src/csharp/keys/Grpc.public.snk Binary files differnew file mode 100644 index 0000000000..bac3046b36 --- /dev/null +++ b/src/csharp/keys/Grpc.public.snk diff --git a/src/csharp/keys/README.md b/src/csharp/keys/README.md new file mode 100644 index 0000000000..f3e9a3cb56 --- /dev/null +++ b/src/csharp/keys/README.md @@ -0,0 +1,5 @@ +Contents +-------- + +- Grpc.public.snk: + Public key to verify strong name of gRPC assemblies.
\ No newline at end of file |