diff options
Diffstat (limited to 'src/csharp')
74 files changed, 1884 insertions, 580 deletions
diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index 39be35c219..3da9e33e53 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -46,6 +46,8 @@ namespace Grpc.Core.Tests { string host = "localhost"; + string serviceName = "/tests.Test"; + Method<string, string> unaryEchoStringMethod = new Method<string, string>( MethodType.Unary, "/tests.Test/UnaryEchoString", @@ -69,15 +71,15 @@ namespace Grpc.Core.Tests { Server server = new Server(); server.AddServiceDefinition( - ServerServiceDefinition.CreateBuilder("someService") + ServerServiceDefinition.CreateBuilder(serviceName) .AddMethod(unaryEchoStringMethod, HandleUnaryEchoString).Build()); - int port = server.AddPort(host + ":0"); + int port = server.AddListeningPort(host + ":0"); server.Start(); using (Channel channel = new Channel(host + ":" + port)) { - var call = new Call<string, string>(unaryEchoStringMethod, channel); + var call = new Call<string, string>(serviceName, unaryEchoStringMethod, channel, Metadata.Empty); Assert.AreEqual("ABC", Calls.BlockingUnaryCall(call, "ABC", default(CancellationToken))); @@ -92,15 +94,15 @@ namespace Grpc.Core.Tests { Server server = new Server(); server.AddServiceDefinition( - ServerServiceDefinition.CreateBuilder("someService") + ServerServiceDefinition.CreateBuilder(serviceName) .AddMethod(unaryEchoStringMethod, HandleUnaryEchoString).Build()); - int port = server.AddPort(host + ":0"); + int port = server.AddListeningPort(host + ":0"); server.Start(); using (Channel channel = new Channel(host + ":" + port)) { - var call = new Call<string, string>(unaryEchoStringMethod, channel); + var call = new Call<string, string>(serviceName, unaryEchoStringMethod, channel, Metadata.Empty); BenchmarkUtil.RunBenchmark(100, 1000, () => { Calls.BlockingUnaryCall(call, "ABC", default(CancellationToken)); }); } @@ -113,19 +115,22 @@ namespace Grpc.Core.Tests { Server server = new Server(); server.AddServiceDefinition( - ServerServiceDefinition.CreateBuilder("someService").Build()); + ServerServiceDefinition.CreateBuilder(serviceName).Build()); - int port = server.AddPort(host + ":0"); + int port = server.AddListeningPort(host + ":0"); server.Start(); using (Channel channel = new Channel(host + ":" + port)) { - var call = new Call<string, string>(unaryEchoStringMethod, channel); + var call = new Call<string, string>(serviceName, unaryEchoStringMethod, channel, Metadata.Empty); - try { + try + { Calls.BlockingUnaryCall(call, "ABC", default(CancellationToken)); Assert.Fail(); - } catch(RpcException e) { + } + catch (RpcException e) + { Assert.AreEqual(StatusCode.Unimplemented, e.Status.StatusCode); } } @@ -140,4 +145,3 @@ namespace Grpc.Core.Tests } } } - diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj index a365320f05..eac8d16fb1 100644 --- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj +++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj @@ -42,6 +42,7 @@ <Compile Include="GrpcEnvironmentTest.cs" /> <Compile Include="TimespecTest.cs" /> <Compile Include="PInvokeTest.cs" /> + <Compile Include="Internal\MetadataArraySafeHandleTest.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup> @@ -56,4 +57,7 @@ <ItemGroup> <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> </ItemGroup> + <ItemGroup> + <Folder Include="Internal\" /> + </ItemGroup> </Project>
\ No newline at end of file diff --git a/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs b/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs index 596918c231..6a132a5b22 100644 --- a/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs +++ b/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs @@ -68,7 +68,7 @@ namespace Grpc.Core.Tests var tp2 = GrpcEnvironment.ThreadPool; GrpcEnvironment.Shutdown(); - Assert.IsFalse(Object.ReferenceEquals(tp1, tp2)); + Assert.IsFalse(object.ReferenceEquals(tp1, tp2)); } } } diff --git a/src/csharp/Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs b/src/csharp/Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs new file mode 100644 index 0000000000..2f6013483d --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs @@ -0,0 +1,62 @@ +#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; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Internal.Tests +{ + public class MetadataArraySafeHandleTest + { + [Test] + public void CreateEmptyAndDestroy() + { + var metadata = Metadata.CreateBuilder().Build(); + var nativeMetadata = MetadataArraySafeHandle.Create(metadata); + nativeMetadata.Dispose(); + } + + [Test] + public void CreateAndDestroy() + { + var metadata = Metadata.CreateBuilder() + .Add(new Metadata.MetadataEntry("host", "somehost")) + .Add(new Metadata.MetadataEntry("header2", "header value")).Build(); + var nativeMetadata = MetadataArraySafeHandle.Create(metadata); + nativeMetadata.Dispose(); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs index 9db08d2f02..3beffc3955 100644 --- a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs +++ b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs @@ -33,13 +33,13 @@ using System; using System.Diagnostics; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Grpc.Core; using Grpc.Core.Internal; using Grpc.Core.Utils; using NUnit.Framework; -using System.Runtime.InteropServices; namespace Grpc.Core.Tests { @@ -73,14 +73,13 @@ namespace Grpc.Core.Tests { BenchmarkUtil.RunBenchmark( 100000, 1000000, - () => { + () => + { CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.Create(); cq.Dispose(); - } - ); + }); } - /// <summary> /// Approximate results: /// (~80ns Mono Linux) @@ -94,10 +93,10 @@ namespace Grpc.Core.Tests counter = 0; BenchmarkUtil.RunBenchmark( 1000000, 10000000, - () => { + () => + { grpcsharp_test_callback(handler); - } - ); + }); Assert.AreNotEqual(0, counter); } @@ -113,10 +112,10 @@ namespace Grpc.Core.Tests counter = 0; BenchmarkUtil.RunBenchmark( 10000, 10000, - () => { - grpcsharp_test_callback(new CompletionCallbackDelegate(Handler)); - } - ); + () => + { + grpcsharp_test_callback(new CompletionCallbackDelegate(Handler)); + }); Assert.AreNotEqual(0, counter); } @@ -129,15 +128,15 @@ namespace Grpc.Core.Tests { BenchmarkUtil.RunBenchmark( 1000000, 100000000, - () => { + () => + { grpcsharp_test_nop(IntPtr.Zero); - } - ); + }); } - private void Handler(GRPCOpError op, IntPtr ptr) { - counter ++; + private void Handler(GRPCOpError op, IntPtr ptr) + { + counter++; } } } - diff --git a/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs index 499d931d2a..e4328806ad 100644 --- a/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.Core.Tests/Properties/AssemblyInfo.cs @@ -1,8 +1,6 @@ using System.Reflection; using System.Runtime.CompilerServices; -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. [assembly: AssemblyTitle("Grpc.Core.Tests")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] @@ -11,12 +9,4 @@ using System.Runtime.CompilerServices; [assembly: AssemblyCopyright("Google Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. [assembly: AssemblyVersion("0.1.*")] -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/src/csharp/Grpc.Core.Tests/ServerTest.cs b/src/csharp/Grpc.Core.Tests/ServerTest.cs index dd30366f6a..2a1855da67 100644 --- a/src/csharp/Grpc.Core.Tests/ServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ServerTest.cs @@ -47,12 +47,11 @@ namespace Grpc.Core.Tests GrpcEnvironment.Initialize(); Server server = new Server(); - server.AddPort("localhost:0"); + server.AddListeningPort("localhost:0"); server.Start(); server.ShutdownAsync().Wait(); GrpcEnvironment.Shutdown(); } - } } diff --git a/src/csharp/Grpc.Core.Tests/TimespecTest.cs b/src/csharp/Grpc.Core.Tests/TimespecTest.cs index 0ca84ec44b..f5bae6d935 100644 --- a/src/csharp/Grpc.Core.Tests/TimespecTest.cs +++ b/src/csharp/Grpc.Core.Tests/TimespecTest.cs @@ -86,4 +86,3 @@ namespace Grpc.Core.Internal.Tests } } } - diff --git a/src/csharp/Grpc.Core/Call.cs b/src/csharp/Grpc.Core/Call.cs index 72dca68895..fe5f40f5e9 100644 --- a/src/csharp/Grpc.Core/Call.cs +++ b/src/csharp/Grpc.Core/Call.cs @@ -33,33 +33,25 @@ using System; using Grpc.Core.Internal; +using Grpc.Core.Utils; namespace Grpc.Core { public class Call<TRequest, TResponse> { - readonly string methodName; - readonly Func<TRequest, byte[]> requestSerializer; - readonly Func<byte[], TResponse> responseDeserializer; + readonly string name; + readonly Marshaller<TRequest> requestMarshaller; + readonly Marshaller<TResponse> responseMarshaller; readonly Channel channel; + readonly Metadata headers; - public Call(string methodName, - Func<TRequest, byte[]> requestSerializer, - Func<byte[], TResponse> responseDeserializer, - TimeSpan timeout, - Channel channel) { - this.methodName = methodName; - this.requestSerializer = requestSerializer; - this.responseDeserializer = responseDeserializer; - this.channel = channel; - } - - public Call(Method<TRequest, TResponse> method, Channel channel) + public Call(string serviceName, Method<TRequest, TResponse> method, Channel channel, Metadata headers) { - this.methodName = method.Name; - this.requestSerializer = method.RequestMarshaller.Serializer; - this.responseDeserializer = method.ResponseMarshaller.Deserializer; - this.channel = channel; + this.name = Preconditions.CheckNotNull(serviceName) + "/" + method.Name; + this.requestMarshaller = method.RequestMarshaller; + this.responseMarshaller = method.ResponseMarshaller; + this.channel = Preconditions.CheckNotNull(channel); + this.headers = Preconditions.CheckNotNull(headers); } public Channel Channel @@ -70,29 +62,42 @@ namespace Grpc.Core } } - public string MethodName + /// <summary> + /// Full methods name including the service name. + /// </summary> + public string Name { get { - return this.methodName; + return name; } } - public Func<TRequest, byte[]> RequestSerializer + /// <summary> + /// Headers to send at the beginning of the call. + /// </summary> + public Metadata Headers { get { - return this.requestSerializer; + return headers; } } - public Func<byte[], TResponse> ResponseDeserializer + public Marshaller<TRequest> RequestMarshaller { get { - return this.responseDeserializer; + return requestMarshaller; + } + } + + public Marshaller<TResponse> ResponseMarshaller + { + get + { + return responseMarshaller; } } } } - diff --git a/src/csharp/Grpc.Core/Calls.cs b/src/csharp/Grpc.Core/Calls.cs index ee2208e5c2..280387b323 100644 --- a/src/csharp/Grpc.Core/Calls.cs +++ b/src/csharp/Grpc.Core/Calls.cs @@ -38,8 +38,6 @@ using Grpc.Core.Internal; namespace Grpc.Core { - // NOTE: this class is work-in-progress - /// <summary> /// Helper methods for generated stubs to make RPC calls. /// </summary> @@ -47,30 +45,29 @@ namespace Grpc.Core { public static TResponse BlockingUnaryCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, CancellationToken token) { - var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer); - return asyncCall.UnaryCall(call.Channel, call.MethodName, req); + var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); + return asyncCall.UnaryCall(call.Channel, call.Name, req, call.Headers); } public static async Task<TResponse> AsyncUnaryCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, CancellationToken token) { - var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer); - asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.MethodName); - return await asyncCall.UnaryCallAsync(req); + var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); + asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.Name); + return await asyncCall.UnaryCallAsync(req, call.Headers); } public static void AsyncServerStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, IObserver<TResponse> outputs, CancellationToken token) { - var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer); - - asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.MethodName); - asyncCall.StartServerStreamingCall(req, outputs); + var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); + asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.Name); + asyncCall.StartServerStreamingCall(req, outputs, call.Headers); } public static ClientStreamingAsyncResult<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, CancellationToken token) { - var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer); - asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.MethodName); - var task = asyncCall.ClientStreamingCallAsync(); + var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); + asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.Name); + var task = asyncCall.ClientStreamingCallAsync(call.Headers); var inputs = new ClientStreamingInputObserver<TRequest, TResponse>(asyncCall); return new ClientStreamingAsyncResult<TRequest, TResponse>(task, inputs); } @@ -82,16 +79,15 @@ namespace Grpc.Core public static IObserver<TRequest> DuplexStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, IObserver<TResponse> outputs, CancellationToken token) { - var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer); - asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.MethodName); - - asyncCall.StartDuplexStreamingCall(outputs); + var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); + asyncCall.Initialize(call.Channel, GetCompletionQueue(), call.Name); + asyncCall.StartDuplexStreamingCall(outputs, call.Headers); return new ClientStreamingInputObserver<TRequest, TResponse>(asyncCall); } - private static CompletionQueueSafeHandle GetCompletionQueue() { + private static CompletionQueueSafeHandle GetCompletionQueue() + { return GrpcEnvironment.ThreadPool.CompletionQueue; } } } - diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs index 83d965debf..3a42dac1d7 100644 --- a/src/csharp/Grpc.Core/Channel.cs +++ b/src/csharp/Grpc.Core/Channel.cs @@ -36,10 +36,13 @@ using Grpc.Core.Internal; namespace Grpc.Core { + /// <summary> + /// gRPC Channel + /// </summary> public class Channel : IDisposable { readonly ChannelSafeHandle handle; - readonly String target; + readonly string target; /// <summary> /// Creates a channel. diff --git a/src/csharp/Grpc.Core/ChannelArgs.cs b/src/csharp/Grpc.Core/ChannelArgs.cs index 298b6edf20..74ab310e44 100644 --- a/src/csharp/Grpc.Core/ChannelArgs.cs +++ b/src/csharp/Grpc.Core/ChannelArgs.cs @@ -30,6 +30,7 @@ #endregion using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -37,33 +38,18 @@ using Grpc.Core.Internal; namespace Grpc.Core { - // TODO: should we be using the builder pattern? + /// <summary> + /// gRPC channel options. + /// </summary> public class ChannelArgs { public const string SslTargetNameOverrideKey = "grpc.ssl_target_name_override"; - public class Builder - { - Dictionary<string,string> stringArgs = new Dictionary<string,string>(); - // TODO: AddInteger not supported yet. - public Builder AddString(string key, string value) - { - stringArgs.Add(key, value); - return this; - } + readonly ImmutableDictionary<string, string> stringArgs; - public ChannelArgs Build() - { - return new ChannelArgs(stringArgs); - } - } - - Dictionary<string,string> stringArgs; - - private ChannelArgs(Dictionary<string, string> stringArgs) + private ChannelArgs(ImmutableDictionary<string, string> stringArgs) { - // TODO: use immutable dict? - this.stringArgs = new Dictionary<string, string>(stringArgs); + this.stringArgs = stringArgs; } public string GetSslTargetNameOverride() @@ -76,11 +62,28 @@ namespace Grpc.Core return null; } - public static Builder NewBuilder() + public static Builder CreateBuilder() { return new Builder(); } + public class Builder + { + readonly Dictionary<string, string> stringArgs = new Dictionary<string, string>(); + + // TODO: AddInteger not supported yet. + public Builder AddString(string key, string value) + { + stringArgs.Add(key, value); + return this; + } + + public ChannelArgs Build() + { + return new ChannelArgs(stringArgs.ToImmutableDictionary()); + } + } + /// <summary> /// Creates native object for the channel arguments. /// </summary> diff --git a/src/csharp/Grpc.Core/ClientStreamingAsyncResult.cs b/src/csharp/Grpc.Core/ClientStreamingAsyncResult.cs index 44580a1154..65bedb0a33 100644 --- a/src/csharp/Grpc.Core/ClientStreamingAsyncResult.cs +++ b/src/csharp/Grpc.Core/ClientStreamingAsyncResult.cs @@ -67,4 +67,3 @@ namespace Grpc.Core } } } - diff --git a/src/csharp/Grpc.Core/Credentials.cs b/src/csharp/Grpc.Core/Credentials.cs index 5116c277f7..15dd3ef321 100644 --- a/src/csharp/Grpc.Core/Credentials.cs +++ b/src/csharp/Grpc.Core/Credentials.cs @@ -36,6 +36,9 @@ using Grpc.Core.Internal; namespace Grpc.Core { + /// <summary> + /// Client-side credentials. + /// </summary> public abstract class Credentials { /// <summary> @@ -74,4 +77,3 @@ namespace Grpc.Core } } } - diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index 78b6cdde59..78ba32b277 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -31,6 +31,9 @@ </PropertyGroup> <ItemGroup> <Reference Include="System" /> + <Reference Include="System.Collections.Immutable"> + <HintPath>..\packages\System.Collections.Immutable.1.1.34-rc\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath> + </Reference> </ItemGroup> <ItemGroup> <Compile Include="Internal\GrpcLog.cs" /> @@ -54,7 +57,7 @@ <Compile Include="Internal\ServerSafeHandle.cs" /> <Compile Include="Method.cs" /> <Compile Include="ServerCalls.cs" /> - <Compile Include="ServerCallHandler.cs" /> + <Compile Include="Internal\ServerCallHandler.cs" /> <Compile Include="Marshaller.cs" /> <Compile Include="ServerServiceDefinition.cs" /> <Compile Include="Utils\RecordingObserver.cs" /> @@ -74,6 +77,15 @@ <Compile Include="OperationFailedException.cs" /> <Compile Include="Internal\AsyncCall.cs" /> <Compile Include="Utils\Preconditions.cs" /> + <Compile Include="Internal\ServerCredentialsSafeHandle.cs" /> + <Compile Include="ServerCredentials.cs" /> + <Compile Include="Metadata.cs" /> + <Compile Include="Internal\MetadataArraySafeHandle.cs" /> + <Compile Include="Stub\AbstractStub.cs" /> + <Compile Include="Stub\StubConfiguration.cs" /> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> </ItemGroup> <Choose> <!-- Under older versions of Monodevelop, Choose is not supported and is just @@ -88,4 +100,7 @@ <Otherwise /> </Choose> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> + <ItemGroup> + <Folder Include="Stub\" /> + </ItemGroup> </Project>
\ No newline at end of file diff --git a/src/csharp/Grpc.Core/GrpcEnvironment.cs b/src/csharp/Grpc.Core/GrpcEnvironment.cs index d3a8da4729..9c10a42e23 100644 --- a/src/csharp/Grpc.Core/GrpcEnvironment.cs +++ b/src/csharp/Grpc.Core/GrpcEnvironment.cs @@ -63,8 +63,9 @@ namespace Grpc.Core /// lifetime (and call Shutdown once you're done), for the sake of easier testing it's /// allowed to initialize the environment again after it has been successfully shutdown. /// </summary> - public static void Initialize() { - lock(staticLock) + public static void Initialize() + { + lock (staticLock) { if (instance == null) { @@ -79,7 +80,7 @@ namespace Grpc.Core /// </summary> public static void Shutdown() { - lock(staticLock) + lock (staticLock) { if (instance != null) { @@ -133,4 +134,3 @@ namespace Grpc.Core } } } - diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs index 5ae036298b..bc72cb78de 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs @@ -54,7 +54,7 @@ namespace Grpc.Core.Internal TaskCompletionSource<TResponse> unaryResponseTcs; // Set after status is received. Only used for streaming response calls. - Nullable<Status> finishedStatus; + Status? finishedStatus; bool readObserverCompleted; // True if readObserver has already been completed. @@ -64,7 +64,7 @@ namespace Grpc.Core.Internal this.finishedHandler = CreateBatchCompletionCallback(HandleFinished); } - public void Initialize(Channel channel, CompletionQueueSafeHandle cq, String methodName) + public void Initialize(Channel channel, CompletionQueueSafeHandle cq, string methodName) { var call = CallSafeHandle.Create(channel.Handle, cq, methodName, channel.Target, Timespec.InfFuture); InitializeInternal(call); @@ -77,9 +77,9 @@ namespace Grpc.Core.Internal /// <summary> /// Blocking unary request - unary response call. /// </summary> - public TResponse UnaryCall(Channel channel, String methodName, TRequest msg) + public TResponse UnaryCall(Channel channel, string methodName, TRequest msg, Metadata headers) { - using(CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.Create()) + using (CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.Create()) { byte[] payload = UnsafeSerialize(msg); @@ -92,7 +92,11 @@ namespace Grpc.Core.Internal halfcloseRequested = true; readingDone = true; } - call.BlockingUnary(cq, payload, unaryResponseHandler); + + using (var metadataArray = MetadataArraySafeHandle.Create(headers)) + { + call.BlockingUnary(cq, payload, unaryResponseHandler, metadataArray); + } try { @@ -109,7 +113,7 @@ namespace Grpc.Core.Internal /// <summary> /// Starts a unary request - unary response call. /// </summary> - public Task<TResponse> UnaryCallAsync(TRequest msg) + public Task<TResponse> UnaryCallAsync(TRequest msg, Metadata headers) { lock (myLock) { @@ -122,8 +126,10 @@ namespace Grpc.Core.Internal byte[] payload = UnsafeSerialize(msg); unaryResponseTcs = new TaskCompletionSource<TResponse>(); - call.StartUnary(payload, unaryResponseHandler); - + using (var metadataArray = MetadataArraySafeHandle.Create(headers)) + { + call.StartUnary(payload, unaryResponseHandler, metadataArray); + } return unaryResponseTcs.Task; } } @@ -132,7 +138,7 @@ namespace Grpc.Core.Internal /// Starts a streamed request - unary response call. /// Use StartSendMessage and StartSendCloseFromClient to stream requests. /// </summary> - public Task<TResponse> ClientStreamingCallAsync() + public Task<TResponse> ClientStreamingCallAsync(Metadata headers) { lock (myLock) { @@ -142,7 +148,10 @@ namespace Grpc.Core.Internal readingDone = true; unaryResponseTcs = new TaskCompletionSource<TResponse>(); - call.StartClientStreaming(unaryResponseHandler); + using (var metadataArray = MetadataArraySafeHandle.Create(headers)) + { + call.StartClientStreaming(unaryResponseHandler, metadataArray); + } return unaryResponseTcs.Task; } @@ -151,7 +160,7 @@ namespace Grpc.Core.Internal /// <summary> /// Starts a unary request - streamed response call. /// </summary> - public void StartServerStreamingCall(TRequest msg, IObserver<TResponse> readObserver) + public void StartServerStreamingCall(TRequest msg, IObserver<TResponse> readObserver, Metadata headers) { lock (myLock) { @@ -165,7 +174,10 @@ namespace Grpc.Core.Internal byte[] payload = UnsafeSerialize(msg); - call.StartServerStreaming(payload, finishedHandler); + using (var metadataArray = MetadataArraySafeHandle.Create(headers)) + { + call.StartServerStreaming(payload, finishedHandler, metadataArray); + } StartReceiveMessage(); } @@ -175,7 +187,7 @@ namespace Grpc.Core.Internal /// Starts a streaming request - streaming response call. /// Use StartSendMessage and StartSendCloseFromClient to stream requests. /// </summary> - public void StartDuplexStreamingCall(IObserver<TResponse> readObserver) + public void StartDuplexStreamingCall(IObserver<TResponse> readObserver, Metadata headers) { lock (myLock) { @@ -185,7 +197,10 @@ namespace Grpc.Core.Internal this.readObserver = readObserver; - call.StartDuplexStreaming(finishedHandler); + using (var metadataArray = MetadataArraySafeHandle.Create(headers)) + { + call.StartDuplexStreaming(finishedHandler, metadataArray); + } StartReceiveMessage(); } @@ -254,7 +269,7 @@ namespace Grpc.Core.Internal /// </summary> private void HandleUnaryResponse(bool wasError, BatchContextSafeHandleNotOwned ctx) { - lock(myLock) + lock (myLock) { finished = true; halfclosed = true; @@ -264,9 +279,7 @@ namespace Grpc.Core.Internal if (wasError) { - unaryResponseTcs.SetException(new RpcException( - new Status(StatusCode.Internal, "Internal error occured.") - )); + unaryResponseTcs.SetException(new RpcException(new Status(StatusCode.Internal, "Internal error occured."))); return; } diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs index 44d66b394c..15b0cfe249 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs @@ -225,7 +225,7 @@ namespace Grpc.Core.Internal payload = serializer(msg); return true; } - catch(Exception) + catch (Exception) { Console.WriteLine("Exception occured while trying to serialize message"); payload = null; @@ -240,7 +240,7 @@ namespace Grpc.Core.Internal msg = deserializer(payload); return true; } - catch(Exception) + catch (Exception) { Console.WriteLine("Exception occured while trying to deserialize message"); msg = default(TRead); @@ -254,7 +254,7 @@ namespace Grpc.Core.Internal { readObserver.OnNext(value); } - catch(Exception e) + catch (Exception e) { Console.WriteLine("Exception occured while invoking readObserver.OnNext: " + e); } @@ -266,7 +266,7 @@ namespace Grpc.Core.Internal { readObserver.OnCompleted(); } - catch(Exception e) + catch (Exception e) { Console.WriteLine("Exception occured while invoking readObserver.OnCompleted: " + e); } @@ -278,7 +278,7 @@ namespace Grpc.Core.Internal { readObserver.OnError(error); } - catch(Exception e) + catch (Exception e) { Console.WriteLine("Exception occured while invoking readObserver.OnError: " + e); } @@ -290,7 +290,7 @@ namespace Grpc.Core.Internal { completionDelegate(error); } - catch(Exception e) + catch (Exception e) { Console.WriteLine("Exception occured while invoking completion delegate: " + e); } @@ -302,14 +302,15 @@ namespace Grpc.Core.Internal /// </summary> protected CompletionCallbackDelegate CreateBatchCompletionCallback(Action<bool, BatchContextSafeHandleNotOwned> handler) { - return new CompletionCallbackDelegate( (error, batchContextPtr) => { + return new CompletionCallbackDelegate((error, batchContextPtr) => + { try { var ctx = new BatchContextSafeHandleNotOwned(batchContextPtr); bool wasError = (error != GRPCOpError.GRPC_OP_OK); handler(wasError, ctx); } - catch(Exception e) + catch (Exception e) { Console.WriteLine("Caught exception in a native handler: " + e); } @@ -363,7 +364,6 @@ namespace Grpc.Core.Internal { FireCompletion(origCompletionDelegate, null); } - } /// <summary> diff --git a/src/csharp/Grpc.Core/Internal/AsyncCompletion.cs b/src/csharp/Grpc.Core/Internal/AsyncCompletion.cs index b78bb497fa..673b527fb2 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCompletion.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCompletion.cs @@ -91,5 +91,4 @@ namespace Grpc.Core.Internal tcs.SetException(error); } } - }
\ No newline at end of file diff --git a/src/csharp/Grpc.Core/Internal/BatchContextSafeHandleNotOwned.cs b/src/csharp/Grpc.Core/Internal/BatchContextSafeHandleNotOwned.cs index 75cd30e1a2..3c54753756 100644 --- a/src/csharp/Grpc.Core/Internal/BatchContextSafeHandleNotOwned.cs +++ b/src/csharp/Grpc.Core/Internal/BatchContextSafeHandleNotOwned.cs @@ -80,16 +80,18 @@ namespace Grpc.Core.Internal { return null; } - byte[] data = new byte[(int) len]; + byte[] data = new byte[(int)len]; grpcsharp_batch_context_recv_message_to_buffer(this, data, new UIntPtr((ulong)data.Length)); return data; } - public CallSafeHandle GetServerRpcNewCall() { + public CallSafeHandle GetServerRpcNewCall() + { return grpcsharp_batch_context_server_rpc_new_call(this); } - public string GetServerRpcNewMethod() { + public string GetServerRpcNewMethod() + { return Marshal.PtrToStringAnsi(grpcsharp_batch_context_server_rpc_new_method(this)); } } diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs index 61566b5407..14add60c72 100644 --- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs @@ -36,13 +36,14 @@ using Grpc.Core; namespace Grpc.Core.Internal { - internal delegate void CompletionCallbackDelegate(GRPCOpError error,IntPtr batchContextPtr); + internal delegate void CompletionCallbackDelegate(GRPCOpError error, IntPtr batchContextPtr); + /// <summary> /// grpc_call from <grpc/grpc.h> /// </summary> internal class CallSafeHandle : SafeHandleZeroIsInvalid { - const UInt32 GRPC_WRITE_BUFFER_HINT = 1; + const uint GRPC_WRITE_BUFFER_HINT = 1; [DllImport("grpc_csharp_ext.dll")] static extern CallSafeHandle grpcsharp_channel_create_call(ChannelSafeHandle channel, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline); @@ -56,25 +57,28 @@ namespace Grpc.Core.Internal [DllImport("grpc_csharp_ext.dll")] static extern GRPCCallError grpcsharp_call_start_unary(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback, - byte[] send_buffer, UIntPtr send_buffer_len); + byte[] send_buffer, UIntPtr send_buffer_len, MetadataArraySafeHandle metadataArray); [DllImport("grpc_csharp_ext.dll")] static extern void grpcsharp_call_blocking_unary(CallSafeHandle call, CompletionQueueSafeHandle dedicatedCq, [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback, - byte[] send_buffer, UIntPtr send_buffer_len); + byte[] send_buffer, UIntPtr send_buffer_len, MetadataArraySafeHandle metadataArray); [DllImport("grpc_csharp_ext.dll")] static extern GRPCCallError grpcsharp_call_start_client_streaming(CallSafeHandle call, - [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback); + [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback, + MetadataArraySafeHandle metadataArray); [DllImport("grpc_csharp_ext.dll")] static extern GRPCCallError grpcsharp_call_start_server_streaming(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback, - byte[] send_buffer, UIntPtr send_buffer_len); + byte[] send_buffer, UIntPtr send_buffer_len, + MetadataArraySafeHandle metadataArray); [DllImport("grpc_csharp_ext.dll")] static extern GRPCCallError grpcsharp_call_start_duplex_streaming(CallSafeHandle call, - [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback); + [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback, + MetadataArraySafeHandle metadataArray); [DllImport("grpc_csharp_ext.dll")] static extern GRPCCallError grpcsharp_call_send_message(CallSafeHandle call, @@ -108,29 +112,29 @@ namespace Grpc.Core.Internal return grpcsharp_channel_create_call(channel, cq, method, host, deadline); } - public void StartUnary(byte[] payload, CompletionCallbackDelegate callback) + public void StartUnary(byte[] payload, CompletionCallbackDelegate callback, MetadataArraySafeHandle metadataArray) { - AssertCallOk(grpcsharp_call_start_unary(this, callback, payload, new UIntPtr((ulong)payload.Length))); + AssertCallOk(grpcsharp_call_start_unary(this, callback, payload, new UIntPtr((ulong)payload.Length), metadataArray)); } - public void BlockingUnary(CompletionQueueSafeHandle dedicatedCq, byte[] payload, CompletionCallbackDelegate callback) + public void BlockingUnary(CompletionQueueSafeHandle dedicatedCq, byte[] payload, CompletionCallbackDelegate callback, MetadataArraySafeHandle metadataArray) { - grpcsharp_call_blocking_unary(this, dedicatedCq, callback, payload, new UIntPtr((ulong)payload.Length)); + grpcsharp_call_blocking_unary(this, dedicatedCq, callback, payload, new UIntPtr((ulong)payload.Length), metadataArray); } - public void StartClientStreaming(CompletionCallbackDelegate callback) + public void StartClientStreaming(CompletionCallbackDelegate callback, MetadataArraySafeHandle metadataArray) { - AssertCallOk(grpcsharp_call_start_client_streaming(this, callback)); + AssertCallOk(grpcsharp_call_start_client_streaming(this, callback, metadataArray)); } - public void StartServerStreaming(byte[] payload, CompletionCallbackDelegate callback) + public void StartServerStreaming(byte[] payload, CompletionCallbackDelegate callback, MetadataArraySafeHandle metadataArray) { - AssertCallOk(grpcsharp_call_start_server_streaming(this, callback, payload, new UIntPtr((ulong)payload.Length))); + AssertCallOk(grpcsharp_call_start_server_streaming(this, callback, payload, new UIntPtr((ulong)payload.Length), metadataArray)); } - public void StartDuplexStreaming(CompletionCallbackDelegate callback) + public void StartDuplexStreaming(CompletionCallbackDelegate callback, MetadataArraySafeHandle metadataArray) { - AssertCallOk(grpcsharp_call_start_duplex_streaming(this, callback)); + AssertCallOk(grpcsharp_call_start_duplex_streaming(this, callback, metadataArray)); } public void StartSendMessage(byte[] payload, CompletionCallbackDelegate callback) @@ -179,7 +183,7 @@ namespace Grpc.Core.Internal Trace.Assert(callError == GRPCCallError.GRPC_CALL_OK, "Status not GRPC_CALL_OK"); } - private static UInt32 GetFlags(bool buffered) + private static uint GetFlags(bool buffered) { return buffered ? 0 : GRPC_WRITE_BUFFER_HINT; } diff --git a/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs index ca3c21d84c..c69f1a0d02 100644 --- a/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs @@ -74,4 +74,3 @@ namespace Grpc.Core.Internal } } } - diff --git a/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs index 6bff923c55..600d1fc87c 100644 --- a/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs @@ -77,4 +77,3 @@ namespace Grpc.Core.Internal } } } - diff --git a/src/csharp/Grpc.Core/Internal/Enums.cs b/src/csharp/Grpc.Core/Internal/Enums.cs index f363050b07..94a2fd1784 100644 --- a/src/csharp/Grpc.Core/Internal/Enums.cs +++ b/src/csharp/Grpc.Core/Internal/Enums.cs @@ -112,4 +112,3 @@ namespace Grpc.Core.Internal GRPC_OP_ERROR } } - diff --git a/src/csharp/Grpc.Core/Internal/GrpcLog.cs b/src/csharp/Grpc.Core/Internal/GrpcLog.cs index 98768d05c6..2f3c8ad71c 100644 --- a/src/csharp/Grpc.Core/Internal/GrpcLog.cs +++ b/src/csharp/Grpc.Core/Internal/GrpcLog.cs @@ -40,7 +40,7 @@ using System.Threading; namespace Grpc.Core.Internal { - internal delegate void GprLogDelegate(IntPtr fileStringPtr, Int32 line, UInt64 threadId, IntPtr severityStringPtr, IntPtr msgPtr); + internal delegate void GprLogDelegate(IntPtr fileStringPtr, int line, ulong threadId, IntPtr severityStringPtr, IntPtr msgPtr); /// <summary> /// Logs from gRPC C core library can get lost if your application is not a console app. @@ -73,7 +73,7 @@ namespace Grpc.Core.Internal } } - private static void HandleWrite(IntPtr fileStringPtr, Int32 line, UInt64 threadId, IntPtr severityStringPtr, IntPtr msgPtr) + private static void HandleWrite(IntPtr fileStringPtr, int line, ulong threadId, IntPtr severityStringPtr, IntPtr msgPtr) { try { diff --git a/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs index 9e69fe2f43..f4224668f1 100644 --- a/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs +++ b/src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs @@ -51,12 +51,13 @@ namespace Grpc.Core.Internal CompletionQueueSafeHandle cq; - public GrpcThreadPool(int poolSize) { + public GrpcThreadPool(int poolSize) + { this.poolSize = poolSize; } - public void Start() { - + public void Start() + { lock (myLock) { if (cq != null) @@ -73,8 +74,8 @@ namespace Grpc.Core.Internal } } - public void Stop() { - + public void Stop() + { lock (myLock) { cq.Shutdown(); @@ -86,7 +87,6 @@ namespace Grpc.Core.Internal } cq.Dispose(); - } } @@ -116,10 +116,9 @@ namespace Grpc.Core.Internal do { completionType = cq.NextWithCallback(); - } while(completionType != GRPCCompletionType.GRPC_QUEUE_SHUTDOWN); + } + while (completionType != GRPCCompletionType.GRPC_QUEUE_SHUTDOWN); Console.WriteLine("Completion queue has shutdown successfully, thread " + Thread.CurrentThread.Name + " exiting."); } } - } - diff --git a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs new file mode 100644 index 0000000000..c9c4d954c9 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs @@ -0,0 +1,72 @@ +#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> + /// grpc_metadata_array from <grpc/grpc.h> + /// </summary> + internal class MetadataArraySafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("grpc_csharp_ext.dll")] + static extern MetadataArraySafeHandle grpcsharp_metadata_array_create(UIntPtr capacity); + + [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)] + static extern void grpcsharp_metadata_array_add(MetadataArraySafeHandle array, string key, byte[] value, UIntPtr valueLength); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_metadata_array_destroy_full(IntPtr array); + + private MetadataArraySafeHandle() + { + } + + public static MetadataArraySafeHandle Create(Metadata metadata) + { + var entries = metadata.Entries; + var metadataArray = grpcsharp_metadata_array_create(new UIntPtr((ulong)entries.Count)); + for (int i = 0; i < entries.Count; i++) + { + grpcsharp_metadata_array_add(metadataArray, entries[i].Key, entries[i].ValueBytes, new UIntPtr((ulong)entries[i].ValueBytes.Length)); + } + return metadataArray; + } + + protected override bool ReleaseHandle() + { + grpcsharp_metadata_array_destroy_full(handle); + return true; + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/SafeHandleZeroIsInvalid.cs b/src/csharp/Grpc.Core/Internal/SafeHandleZeroIsInvalid.cs index aa6fce2e96..702aea2883 100644 --- a/src/csharp/Grpc.Core/Internal/SafeHandleZeroIsInvalid.cs +++ b/src/csharp/Grpc.Core/Internal/SafeHandleZeroIsInvalid.cs @@ -64,4 +64,3 @@ namespace Grpc.Core.Internal } } } - diff --git a/src/csharp/Grpc.Core/ServerCallHandler.cs b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs index 3eb8422f57..25fd4fab8f 100644 --- a/src/csharp/Grpc.Core/ServerCallHandler.cs +++ b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs @@ -36,7 +36,7 @@ using System.Linq; using Grpc.Core.Internal; using Grpc.Core.Utils; -namespace Grpc.Core +namespace Grpc.Core.Internal { internal interface IServerCallHandler { @@ -70,7 +70,6 @@ namespace Grpc.Core handler(request, responseObserver); finishedTask.Wait(); - } } @@ -93,7 +92,7 @@ namespace Grpc.Core asyncCall.Initialize(call); - var responseObserver = new ServerStreamingOutputObserver<TRequest,TResponse>(asyncCall); + var responseObserver = new ServerStreamingOutputObserver<TRequest, TResponse>(asyncCall); var requestObserver = handler(responseObserver); var finishedTask = asyncCall.ServerSideCallAsync(requestObserver); finishedTask.Wait(); @@ -113,7 +112,7 @@ namespace Grpc.Core var finishedTask = asyncCall.ServerSideCallAsync(new NullObserver<byte[]>()); // TODO: check result of the completion status. - asyncCall.StartSendStatusFromServer(new Status(StatusCode.Unimplemented, "No such method."), new AsyncCompletionDelegate((error) => {})); + asyncCall.StartSendStatusFromServer(new Status(StatusCode.Unimplemented, "No such method."), new AsyncCompletionDelegate((error) => { })); finishedTask.Wait(); } @@ -132,7 +131,5 @@ namespace Grpc.Core public void OnNext(T value) { } - } } - diff --git a/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs new file mode 100644 index 0000000000..961180741a --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs @@ -0,0 +1,68 @@ +#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; +using System.Threading.Tasks; +using Grpc.Core.Utils; + +namespace Grpc.Core.Internal +{ + /// <summary> + /// grpc_server_credentials from <grpc/grpc_security.h> + /// </summary> + 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); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_server_credentials_release(IntPtr credentials); + + private ServerCredentialsSafeHandle() + { + } + + public static ServerCredentialsSafeHandle CreateSslCredentials(string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray) + { + Preconditions.CheckArgument(keyCertPairCertChainArray.Length == keyCertPairPrivateKeyArray.Length); + return grpcsharp_ssl_server_credentials_create(null, + keyCertPairCertChainArray, keyCertPairPrivateKeyArray, + new UIntPtr((ulong)keyCertPairCertChainArray.Length)); + } + + protected override bool ReleaseHandle() + { + grpcsharp_server_credentials_release(handle); + return true; + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs index de9bbaf7c1..a59da09822 100644 --- a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs @@ -53,7 +53,10 @@ namespace Grpc.Core.Internal static extern ServerSafeHandle grpcsharp_server_create(CompletionQueueSafeHandle cq, IntPtr args); [DllImport("grpc_csharp_ext.dll")] - static extern Int32 grpcsharp_server_add_http2_port(ServerSafeHandle server, string addr); + static extern int grpcsharp_server_add_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); [DllImport("grpc_csharp_ext.dll")] static extern void grpcsharp_server_start(ServerSafeHandle server); @@ -74,15 +77,19 @@ namespace Grpc.Core.Internal public static ServerSafeHandle NewServer(CompletionQueueSafeHandle cq, IntPtr args) { - // TODO: also grpc_secure_server_create... return grpcsharp_server_create(cq, args); } - public int AddPort(string addr) + public int AddListeningPort(string addr) { return grpcsharp_server_add_http2_port(this, addr); } + public int AddListeningPort(string addr, ServerCredentialsSafeHandle credentials) + { + return grpcsharp_server_add_secure_http2_port(this, addr, credentials); + } + public void Start() { grpcsharp_server_start(this); diff --git a/src/csharp/Grpc.Core/Internal/ServerStreamingOutputObserver.cs b/src/csharp/Grpc.Core/Internal/ServerStreamingOutputObserver.cs index 9873dc9c71..97b62d0569 100644 --- a/src/csharp/Grpc.Core/Internal/ServerStreamingOutputObserver.cs +++ b/src/csharp/Grpc.Core/Internal/ServerStreamingOutputObserver.cs @@ -69,4 +69,3 @@ namespace Grpc.Core.Internal } } } - diff --git a/src/csharp/Grpc.Core/Internal/Timespec.cs b/src/csharp/Grpc.Core/Internal/Timespec.cs index e6efd66f13..94d48c2c49 100644 --- a/src/csharp/Grpc.Core/Internal/Timespec.cs +++ b/src/csharp/Grpc.Core/Internal/Timespec.cs @@ -40,8 +40,8 @@ namespace Grpc.Core.Internal [StructLayout(LayoutKind.Sequential)] internal struct Timespec { - const int nanosPerSecond = 1000 * 1000 * 1000; - const int nanosPerTick = 100; + const int NanosPerSecond = 1000 * 1000 * 1000; + const int NanosPerTick = 100; [DllImport("grpc_csharp_ext.dll")] static extern Timespec gprsharp_now(); @@ -99,14 +99,13 @@ namespace Grpc.Core.Internal public Timespec Add(TimeSpan timeSpan) { - long nanos = tv_nsec.ToInt64() + (timeSpan.Ticks % TimeSpan.TicksPerSecond) * nanosPerTick; - long overflow_sec = (nanos > nanosPerSecond) ? 1 : 0; + long nanos = tv_nsec.ToInt64() + (timeSpan.Ticks % TimeSpan.TicksPerSecond) * NanosPerTick; + long overflow_sec = (nanos > NanosPerSecond) ? 1 : 0; Timespec result; - result.tv_nsec = new IntPtr(nanos % nanosPerSecond); + result.tv_nsec = new IntPtr(nanos % NanosPerSecond); result.tv_sec = new IntPtr(tv_sec.ToInt64() + (timeSpan.Ticks / TimeSpan.TicksPerSecond) + overflow_sec); return result; } } } - diff --git a/src/csharp/Grpc.Core/Marshaller.cs b/src/csharp/Grpc.Core/Marshaller.cs index 602e0eb824..8b1a929074 100644 --- a/src/csharp/Grpc.Core/Marshaller.cs +++ b/src/csharp/Grpc.Core/Marshaller.cs @@ -32,6 +32,7 @@ #endregion using System; +using Grpc.Core.Utils; namespace Grpc.Core { @@ -40,13 +41,13 @@ namespace Grpc.Core /// </summary> public struct Marshaller<T> { - readonly Func<T,byte[]> serializer; - readonly Func<byte[],T> deserializer; + readonly Func<T, byte[]> serializer; + readonly Func<byte[], T> deserializer; public Marshaller(Func<T, byte[]> serializer, Func<byte[], T> deserializer) { - this.serializer = serializer; - this.deserializer = deserializer; + this.serializer = Preconditions.CheckNotNull(serializer); + this.deserializer = Preconditions.CheckNotNull(deserializer); } public Func<T, byte[]> Serializer @@ -66,9 +67,12 @@ namespace Grpc.Core } } - public static class Marshallers { - - public static Marshaller<T> Create<T>(Func<T,byte[]> serializer, Func<byte[],T> deserializer) + /// <summary> + /// Utilities for creating marshallers. + /// </summary> + public static class Marshallers + { + public static Marshaller<T> Create<T>(Func<T, byte[]> serializer, Func<byte[], T> deserializer) { return new Marshaller<T>(serializer, deserializer); } @@ -81,7 +85,5 @@ namespace Grpc.Core System.Text.Encoding.UTF8.GetString); } } - } } - diff --git a/src/csharp/Grpc.Core/Metadata.cs b/src/csharp/Grpc.Core/Metadata.cs new file mode 100644 index 0000000000..eccec26a61 --- /dev/null +++ b/src/csharp/Grpc.Core/Metadata.cs @@ -0,0 +1,126 @@ +#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.Collections.Immutable; +using System.Runtime.InteropServices; +using System.Text; + +namespace Grpc.Core +{ + /// <summary> + /// gRPC call metadata. + /// </summary> + public class Metadata + { + public static readonly Metadata Empty = new Metadata(ImmutableList<MetadataEntry>.Empty); + + readonly ImmutableList<MetadataEntry> entries; + + public Metadata(ImmutableList<MetadataEntry> entries) + { + this.entries = entries; + } + + public ImmutableList<MetadataEntry> Entries + { + get + { + return this.entries; + } + } + + public static Builder CreateBuilder() + { + return new Builder(); + } + + public struct MetadataEntry + { + readonly string key; + readonly byte[] valueBytes; + + public MetadataEntry(string key, byte[] valueBytes) + { + this.key = key; + this.valueBytes = valueBytes; + } + + public MetadataEntry(string key, string value) + { + this.key = key; + this.valueBytes = Encoding.ASCII.GetBytes(value); + } + + public string Key + { + get + { + return this.key; + } + } + + // TODO: using ByteString would guarantee immutability. + public byte[] ValueBytes + { + get + { + return this.valueBytes; + } + } + } + + public class Builder + { + readonly List<Metadata.MetadataEntry> entries = new List<Metadata.MetadataEntry>(); + + public List<MetadataEntry> Entries + { + get + { + return entries; + } + } + + public Builder Add(MetadataEntry entry) + { + entries.Add(entry); + return this; + } + + public Metadata Build() + { + return new Metadata(entries.ToImmutableList()); + } + } + } +} diff --git a/src/csharp/Grpc.Core/Method.cs b/src/csharp/Grpc.Core/Method.cs index c94aa8161f..4f97eeef37 100644 --- a/src/csharp/Grpc.Core/Method.cs +++ b/src/csharp/Grpc.Core/Method.cs @@ -94,4 +94,3 @@ namespace Grpc.Core } } } - diff --git a/src/csharp/Grpc.Core/OperationFailedException.cs b/src/csharp/Grpc.Core/OperationFailedException.cs index 34a8c95a85..9b1c24d0c1 100644 --- a/src/csharp/Grpc.Core/OperationFailedException.cs +++ b/src/csharp/Grpc.Core/OperationFailedException.cs @@ -45,4 +45,3 @@ namespace Grpc.Core } } } - diff --git a/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs index 37ba1e2263..168939cf8c 100644 --- a/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.Core/Properties/AssemblyInfo.cs @@ -1,24 +1,14 @@ using System.Reflection; using System.Runtime.CompilerServices; -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. -[assembly: AssemblyTitle ("Grpc.Core")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("Google Inc. All rights reserved.")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. -[assembly: AssemblyVersion ("0.1.*")] -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] +[assembly: AssemblyTitle("Grpc.Core")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("0.1.*")] [assembly: InternalsVisibleTo("Grpc.Core.Tests")] - diff --git a/src/csharp/Grpc.Core/RpcException.cs b/src/csharp/Grpc.Core/RpcException.cs index e1cf64ca56..433d87215e 100644 --- a/src/csharp/Grpc.Core/RpcException.cs +++ b/src/csharp/Grpc.Core/RpcException.cs @@ -35,6 +35,9 @@ using System; namespace Grpc.Core { + /// <summary> + /// Thrown when remote procedure call fails. + /// </summary> public class RpcException : Exception { private readonly Status status; @@ -58,4 +61,3 @@ namespace Grpc.Core } } } - diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs index 152cc2176c..f086fa8beb 100644 --- a/src/csharp/Grpc.Core/Server.cs +++ b/src/csharp/Grpc.Core/Server.cs @@ -67,16 +67,27 @@ namespace Grpc.Core } // only call this before Start() - public void AddServiceDefinition(ServerServiceDefinition serviceDefinition) { - foreach(var entry in serviceDefinition.CallHandlers) + public void AddServiceDefinition(ServerServiceDefinition serviceDefinition) + { + foreach (var entry in serviceDefinition.CallHandlers) { callHandlers.Add(entry.Key, entry.Value); } } // only call before Start() - public int AddPort(string addr) { - return handle.AddPort(addr); + public int AddListeningPort(string addr) + { + return handle.AddListeningPort(addr); + } + + // only call before Start() + public int AddListeningPort(string addr, ServerCredentials credentials) + { + using (var nativeCredentials = credentials.ToNativeCredentials()) + { + return handle.AddListeningPort(addr, nativeCredentials); + } } public void Start() @@ -98,7 +109,7 @@ namespace Grpc.Core { var rpcInfo = newRpcQueue.Take(); - //Console.WriteLine("Server received RPC " + rpcInfo.Method); + // Console.WriteLine("Server received RPC " + rpcInfo.Method); IServerCallHandler callHandler; if (!callHandlers.TryGetValue(rpcInfo.Method, out callHandler)) @@ -107,7 +118,7 @@ namespace Grpc.Core } callHandler.StartCall(rpcInfo.Method, rpcInfo.Call, GetCompletionQueue()); } - catch(Exception e) + catch (Exception e) { Console.WriteLine("Exception while handling RPC: " + e); } @@ -118,7 +129,8 @@ namespace Grpc.Core /// cleans up used resources. /// </summary> /// <returns>The async.</returns> - public async Task ShutdownAsync() { + public async Task ShutdownAsync() + { handle.ShutdownAndNotify(serverShutdownHandler); await shutdownTcs.Task; handle.Dispose(); @@ -135,11 +147,13 @@ namespace Grpc.Core } } - public void Kill() { + public void Kill() + { handle.Dispose(); } - private async Task StartHandlingRpcs() { + private async Task StartHandlingRpcs() + { while (true) { await Task.Factory.StartNew(RunRpc); @@ -151,22 +165,27 @@ namespace Grpc.Core AssertCallOk(handle.RequestCall(GetCompletionQueue(), newServerRpcHandler)); } - private void HandleNewServerRpc(GRPCOpError error, IntPtr batchContextPtr) { - try { + private void HandleNewServerRpc(GRPCOpError error, IntPtr batchContextPtr) + { + try + { var ctx = new BatchContextSafeHandleNotOwned(batchContextPtr); - if (error != GRPCOpError.GRPC_OP_OK) { + if (error != GRPCOpError.GRPC_OP_OK) + { // TODO: handle error } var rpcInfo = new NewRpcInfo(ctx.GetServerRpcNewCall(), ctx.GetServerRpcNewMethod()); // after server shutdown, the callback returns with null call - if (!rpcInfo.Call.IsInvalid) { + if (!rpcInfo.Call.IsInvalid) + { newRpcQueue.Add(rpcInfo); } - - } catch(Exception e) { + } + catch (Exception e) + { Console.WriteLine("Caught exception in a native handler: " + e); } } diff --git a/src/csharp/Grpc.Core/ServerCalls.cs b/src/csharp/Grpc.Core/ServerCalls.cs index bed77796de..dcae99446f 100644 --- a/src/csharp/Grpc.Core/ServerCalls.cs +++ b/src/csharp/Grpc.Core/ServerCalls.cs @@ -32,17 +32,18 @@ #endregion using System; +using Grpc.Core.Internal; namespace Grpc.Core { // TODO: perhaps add also serverSideStreaming and clientSideStreaming - public delegate void UnaryRequestServerMethod<TRequest, TResponse> (TRequest request, IObserver<TResponse> responseObserver); + public delegate void UnaryRequestServerMethod<TRequest, TResponse>(TRequest request, IObserver<TResponse> responseObserver); - public delegate IObserver<TRequest> StreamingRequestServerMethod<TRequest, TResponse> (IObserver<TResponse> responseObserver); - - internal static class ServerCalls { + public delegate IObserver<TRequest> StreamingRequestServerMethod<TRequest, TResponse>(IObserver<TResponse> responseObserver); + internal static class ServerCalls + { public static IServerCallHandler UnaryRequestCall<TRequest, TResponse>(Method<TRequest, TResponse> method, UnaryRequestServerMethod<TRequest, TResponse> handler) { return new UnaryRequestServerCallHandler<TRequest, TResponse>(method, handler); @@ -52,7 +53,5 @@ namespace Grpc.Core { return new StreamingRequestServerCallHandler<TRequest, TResponse>(method, handler); } - } } - diff --git a/src/csharp/Grpc.Core/ServerCredentials.cs b/src/csharp/Grpc.Core/ServerCredentials.cs new file mode 100644 index 0000000000..ab7d0b4914 --- /dev/null +++ b/src/csharp/Grpc.Core/ServerCredentials.cs @@ -0,0 +1,109 @@ +#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.Collections.Immutable; +using Grpc.Core.Internal; + +namespace Grpc.Core +{ + /// <summary> + /// Server side credentials. + /// </summary> + public abstract class ServerCredentials + { + /// <summary> + /// Creates native object for the credentials. + /// </summary> + /// <returns>The native credentials.</returns> + internal abstract ServerCredentialsSafeHandle ToNativeCredentials(); + } + + /// <summary> + /// Key certificate pair (in PEM encoding). + /// </summary> + public class KeyCertificatePair + { + readonly string certChain; + readonly string privateKey; + + public KeyCertificatePair(string certChain, string privateKey) + { + this.certChain = certChain; + this.privateKey = privateKey; + } + + public string CertChain + { + get + { + return certChain; + } + } + + public string PrivateKey + { + get + { + return privateKey; + } + } + } + + /// <summary> + /// Server-side SSL credentials. + /// </summary> + public class SslServerCredentials : ServerCredentials + { + ImmutableList<KeyCertificatePair> keyCertPairs; + + public SslServerCredentials(ImmutableList<KeyCertificatePair> keyCertPairs) + { + this.keyCertPairs = keyCertPairs; + } + + internal override ServerCredentialsSafeHandle ToNativeCredentials() + { + int count = keyCertPairs.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; + } + return ServerCredentialsSafeHandle.CreateSslCredentials(certChains, keys); + } + } +} diff --git a/src/csharp/Grpc.Core/ServerServiceDefinition.cs b/src/csharp/Grpc.Core/ServerServiceDefinition.cs index 231c376062..f08c7d88f3 100644 --- a/src/csharp/Grpc.Core/ServerServiceDefinition.cs +++ b/src/csharp/Grpc.Core/ServerServiceDefinition.cs @@ -33,22 +33,24 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; +using Grpc.Core.Internal; namespace Grpc.Core { + /// <summary> + /// Mapping of method names to server call handlers. + /// </summary> public class ServerServiceDefinition { - readonly string serviceName; - // TODO: we would need an immutable dictionary here... - readonly Dictionary<string, IServerCallHandler> callHandlers; + readonly ImmutableDictionary<string, IServerCallHandler> callHandlers; - private ServerServiceDefinition(string serviceName, Dictionary<string, IServerCallHandler> callHandlers) + private ServerServiceDefinition(ImmutableDictionary<string, IServerCallHandler> callHandlers) { - this.serviceName = serviceName; - this.callHandlers = new Dictionary<string, IServerCallHandler>(callHandlers); + this.callHandlers = callHandlers; } - internal Dictionary<string, IServerCallHandler> CallHandlers + internal ImmutableDictionary<string, IServerCallHandler> CallHandlers { get { @@ -56,8 +58,7 @@ namespace Grpc.Core } } - - public static Builder CreateBuilder(String serviceName) + public static Builder CreateBuilder(string serviceName) { return new Builder(serviceName); } @@ -65,7 +66,7 @@ namespace Grpc.Core public class Builder { readonly string serviceName; - readonly Dictionary<string, IServerCallHandler> callHandlers = new Dictionary<String, IServerCallHandler>(); + readonly Dictionary<string, IServerCallHandler> callHandlers = new Dictionary<string, IServerCallHandler>(); public Builder(string serviceName) { @@ -76,7 +77,7 @@ namespace Grpc.Core Method<TRequest, TResponse> method, UnaryRequestServerMethod<TRequest, TResponse> handler) { - callHandlers.Add(method.Name, ServerCalls.UnaryRequestCall(method, handler)); + callHandlers.Add(GetFullMethodName(serviceName, method.Name), ServerCalls.UnaryRequestCall(method, handler)); return this; } @@ -84,15 +85,19 @@ namespace Grpc.Core Method<TRequest, TResponse> method, StreamingRequestServerMethod<TRequest, TResponse> handler) { - callHandlers.Add(method.Name, ServerCalls.StreamingRequestCall(method, handler)); + callHandlers.Add(GetFullMethodName(serviceName, method.Name), ServerCalls.StreamingRequestCall(method, handler)); return this; } public ServerServiceDefinition Build() { - return new ServerServiceDefinition(serviceName, callHandlers); + return new ServerServiceDefinition(callHandlers.ToImmutableDictionary()); + } + + private string GetFullMethodName(string serviceName, string methodName) + { + return serviceName + "/" + methodName; } } } } - diff --git a/src/csharp/Grpc.Core/StatusCode.cs b/src/csharp/Grpc.Core/StatusCode.cs index 1987e52789..a9696fa469 100644 --- a/src/csharp/Grpc.Core/StatusCode.cs +++ b/src/csharp/Grpc.Core/StatusCode.cs @@ -35,9 +35,9 @@ using System; namespace Grpc.Core { - // TODO: element names should changed to comply with C# naming conventions. /// <summary> - /// based on grpc_status_code from grpc/status.h + /// Result of a remote procedure call. + /// Based on grpc_status_code from grpc/status.h /// </summary> public enum StatusCode { @@ -139,4 +139,3 @@ namespace Grpc.Core DataLoss = 15 } } - diff --git a/src/csharp/Grpc.Core/Stub/AbstractStub.cs b/src/csharp/Grpc.Core/Stub/AbstractStub.cs new file mode 100644 index 0000000000..cf5ab958c5 --- /dev/null +++ b/src/csharp/Grpc.Core/Stub/AbstractStub.cs @@ -0,0 +1,73 @@ +#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.Internal; + +namespace Grpc.Core +{ + // TODO: support adding timeout to methods. + /// <summary> + /// Base for client-side stubs. + /// </summary> + public abstract class AbstractStub<TStub, TConfig> + where TConfig : StubConfiguration + { + readonly Channel channel; + readonly TConfig config; + + public AbstractStub(Channel channel, TConfig config) + { + this.channel = channel; + this.config = config; + } + + public Channel Channel + { + get + { + return this.channel; + } + } + + /// <summary> + /// Creates a new call to given method. + /// </summary> + protected Call<TRequest, TResponse> CreateCall<TRequest, TResponse>(string serviceName, Method<TRequest, TResponse> method) + { + var headerBuilder = Metadata.CreateBuilder(); + config.HeaderInterceptor(headerBuilder); + return new Call<TRequest, TResponse>(serviceName, method, channel, headerBuilder.Build()); + } + } +} diff --git a/src/csharp/Grpc.Core/Stub/StubConfiguration.cs b/src/csharp/Grpc.Core/Stub/StubConfiguration.cs new file mode 100644 index 0000000000..5bcb5b40d2 --- /dev/null +++ b/src/csharp/Grpc.Core/Stub/StubConfiguration.cs @@ -0,0 +1,64 @@ +#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.Internal; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + public delegate void HeaderInterceptorDelegate(Metadata.Builder headerBuilder); + + public class StubConfiguration + { + /// <summary> + /// The default stub configuration. + /// </summary> + public static readonly StubConfiguration Default = new StubConfiguration((headerBuilder) => { }); + + readonly HeaderInterceptorDelegate headerInterceptor; + + public StubConfiguration(HeaderInterceptorDelegate headerInterceptor) + { + this.headerInterceptor = Preconditions.CheckNotNull(headerInterceptor); + } + + public HeaderInterceptorDelegate HeaderInterceptor + { + get + { + return headerInterceptor; + } + } + } +} diff --git a/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs b/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs index 3f0dae84cf..4180d98938 100644 --- a/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs +++ b/src/csharp/Grpc.Core/Utils/BenchmarkUtil.cs @@ -32,10 +32,10 @@ #endregion using System; -using System.Threading.Tasks; -using System.Collections.Generic; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics; +using System.Threading.Tasks; namespace Grpc.Core.Utils { @@ -61,8 +61,7 @@ namespace Grpc.Core.Utils } stopwatch.Stop(); Console.WriteLine("Elapsed time: " + stopwatch.ElapsedMilliseconds + "ms"); - Console.WriteLine("Ops per second: " + (int) ((double) benchmarkIterations * 1000 / stopwatch.ElapsedMilliseconds)); + Console.WriteLine("Ops per second: " + (int)((double)benchmarkIterations * 1000 / stopwatch.ElapsedMilliseconds)); } } } - diff --git a/src/csharp/Grpc.Core/Utils/ExceptionHelper.cs b/src/csharp/Grpc.Core/Utils/ExceptionHelper.cs index 18702e1cc4..c4d6bee058 100644 --- a/src/csharp/Grpc.Core/Utils/ExceptionHelper.cs +++ b/src/csharp/Grpc.Core/Utils/ExceptionHelper.cs @@ -42,7 +42,8 @@ namespace Grpc.Core.Utils /// Otherwise, rethrows the original aggregate exception. /// Always throws, the exception return type is here only to make the. /// </summary> - public static Exception UnwrapRpcException(AggregateException ae) { + public static Exception UnwrapRpcException(AggregateException ae) + { foreach (var e in ae.InnerExceptions) { if (e is RpcException) @@ -54,4 +55,3 @@ namespace Grpc.Core.Utils } } } - diff --git a/src/csharp/Grpc.Core/Utils/Preconditions.cs b/src/csharp/Grpc.Core/Utils/Preconditions.cs index b17ce42117..aeb5d210a7 100644 --- a/src/csharp/Grpc.Core/Utils/Preconditions.cs +++ b/src/csharp/Grpc.Core/Utils/Preconditions.cs @@ -32,10 +32,10 @@ #endregion using System; -using System.Threading.Tasks; -using System.Collections.Generic; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics; +using System.Threading.Tasks; namespace Grpc.Core.Utils { @@ -66,7 +66,7 @@ namespace Grpc.Core.Utils /// <summary> /// Throws NullReferenceException if reference is null. /// </summary> - public static T CheckNotNull<T> (T reference) + public static T CheckNotNull<T>(T reference) { if (reference == null) { @@ -78,7 +78,7 @@ namespace Grpc.Core.Utils /// <summary> /// Throws NullReferenceException with given message if reference is null. /// </summary> - public static T CheckNotNull<T> (T reference, string errorMessage) + public static T CheckNotNull<T>(T reference, string errorMessage) { if (reference == null) { @@ -110,4 +110,3 @@ namespace Grpc.Core.Utils } } } - diff --git a/src/csharp/Grpc.Core/Utils/RecordingObserver.cs b/src/csharp/Grpc.Core/Utils/RecordingObserver.cs index 99d2725b70..7b43ab8ad5 100644 --- a/src/csharp/Grpc.Core/Utils/RecordingObserver.cs +++ b/src/csharp/Grpc.Core/Utils/RecordingObserver.cs @@ -57,9 +57,9 @@ namespace Grpc.Core.Utils data.Add(value); } - public Task<List<T>> ToList() { + public Task<List<T>> ToList() + { return tcs.Task; } } } - diff --git a/src/csharp/Grpc.Core/Utils/RecordingQueue.cs b/src/csharp/Grpc.Core/Utils/RecordingQueue.cs index 63992da6a9..9749168af0 100644 --- a/src/csharp/Grpc.Core/Utils/RecordingQueue.cs +++ b/src/csharp/Grpc.Core/Utils/RecordingQueue.cs @@ -32,9 +32,9 @@ #endregion using System; -using System.Threading.Tasks; -using System.Collections.Generic; using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading.Tasks; namespace Grpc.Core.Utils { @@ -81,4 +81,3 @@ namespace Grpc.Core.Utils } } } - diff --git a/src/csharp/Grpc.Core/packages.config b/src/csharp/Grpc.Core/packages.config new file mode 100644 index 0000000000..cf711ac362 --- /dev/null +++ b/src/csharp/Grpc.Core/packages.config @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="System.Collections.Immutable" version="1.1.34-rc" targetFramework="net45" /> +</packages>
\ No newline at end of file diff --git a/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs index bdd7189db2..11fc099a95 100644 --- a/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.Examples.MathClient/Properties/AssemblyInfo.cs @@ -1,22 +1,12 @@ using System.Reflection; using System.Runtime.CompilerServices; -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. -[assembly: AssemblyTitle ("Grpc.Examples.MathClient")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("Google Inc. All rights reserved.")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. -[assembly: AssemblyVersion ("0.1.*")] -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - +[assembly: AssemblyTitle("Grpc.Examples.MathClient")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("0.1.*")] diff --git a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs index 767340d6f2..85f213cb39 100644 --- a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs +++ b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs @@ -58,10 +58,17 @@ namespace math.Tests server = new Server(); server.AddServiceDefinition(MathGrpc.BindService(new MathServiceImpl())); - int port = server.AddPort(host + ":0"); + int port = server.AddListeningPort(host + ":0"); server.Start(); channel = new Channel(host + ":" + port); - client = MathGrpc.NewStub(channel); + + // TODO: get rid of the custom header here once we have dedicated tests + // for header support. + var stubConfig = new StubConfiguration((headerBuilder) => + { + headerBuilder.Add(new Metadata.MetadataEntry("customHeader", "abcdef")); + }); + client = MathGrpc.NewStub(channel, stubConfig); } [TestFixtureTearDown] @@ -105,7 +112,7 @@ namespace math.Tests var recorder = new RecordingObserver<Num>(); client.Fib(new FibArgs.Builder { Limit = 6 }.Build(), recorder); - CollectionAssert.AreEqual(new List<long>{1, 1, 2, 3, 5, 8}, + CollectionAssert.AreEqual(new List<long> { 1, 1, 2, 3, 5, 8 }, recorder.ToList().Result.ConvertAll((n) => n.Num_)); } @@ -114,7 +121,8 @@ namespace math.Tests public void Sum() { var res = client.Sum(); - foreach (var num in new long[] { 10, 20, 30 }) { + foreach (var num in new long[] { 10, 20, 30 }) + { res.Inputs.OnNext(Num.CreateBuilder().SetNum_(num).Build()); } res.Inputs.OnCompleted(); @@ -125,7 +133,8 @@ namespace math.Tests [Test] public void DivMany() { - List<DivArgs> divArgsList = new List<DivArgs>{ + List<DivArgs> 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() @@ -142,9 +151,8 @@ namespace math.Tests var result = recorder.ToList().Result; - CollectionAssert.AreEqual(new long[] {3, 4, 3}, result.ConvertAll((divReply) => divReply.Quotient)); - CollectionAssert.AreEqual(new long[] {1, 16, 1}, result.ConvertAll((divReply) => divReply.Remainder)); + 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.Tests/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs index 44b075ac0a..43c7616ac3 100644 --- a/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.Examples.Tests/Properties/AssemblyInfo.cs @@ -1,8 +1,6 @@ using System.Reflection; using System.Runtime.CompilerServices; -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. [assembly: AssemblyTitle("Grpc.Examples.Tests")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] @@ -11,12 +9,4 @@ using System.Runtime.CompilerServices; [assembly: AssemblyCopyright("Google Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. [assembly: AssemblyVersion("0.1.*")] -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/src/csharp/Grpc.Examples/MathExamples.cs b/src/csharp/Grpc.Examples/MathExamples.cs index 134270f6f7..b8bb7eacbd 100644 --- a/src/csharp/Grpc.Examples/MathExamples.cs +++ b/src/csharp/Grpc.Examples/MathExamples.cs @@ -71,7 +71,8 @@ namespace math public static void SumExample(MathGrpc.IMathServiceClient stub) { List<Num> numbers = new List<Num> - {new Num.Builder { Num_ = 1 }.Build(), + { + new Num.Builder { Num_ = 1 }.Build(), new Num.Builder { Num_ = 2 }.Build(), new Num.Builder { Num_ = 3 }.Build() }; @@ -110,24 +111,12 @@ namespace math public static void DependendRequestsExample(MathGrpc.IMathServiceClient stub) { var numberList = new List<Num> - { new Num.Builder{ Num_ = 1 }.Build(), - new Num.Builder{ Num_ = 2 }.Build(), new Num.Builder{ Num_ = 3 }.Build() + { + new Num.Builder { Num_ = 1 }.Build(), + new Num.Builder { Num_ = 2 }.Build(), new Num.Builder { Num_ = 3 }.Build() }; numberList.ToObservable(); - - //IObserver<Num> numbers; - //Task<Num> call = stub.Sum(out numbers); - //foreach (var num in numberList) - //{ - // numbers.OnNext(num); - //} - //numbers.OnCompleted(); - - //Num sum = call.Result; - - //DivReply result = stub.Div(new DivArgs.Builder { Dividend = sum.Num_, Divisor = numberList.Count }.Build()); } } } - diff --git a/src/csharp/Grpc.Examples/MathGrpc.cs b/src/csharp/Grpc.Examples/MathGrpc.cs index f938a24543..24e6a1de8e 100644 --- a/src/csharp/Grpc.Examples/MathGrpc.cs +++ b/src/csharp/Grpc.Examples/MathGrpc.cs @@ -45,35 +45,36 @@ namespace math /// </summary> public class MathGrpc { - readonly static Marshaller<DivArgs> divArgsMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), DivArgs.ParseFrom); - readonly static Marshaller<DivReply> divReplyMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), DivReply.ParseFrom); - readonly static Marshaller<Num> numMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), Num.ParseFrom); - readonly static Marshaller<FibArgs> fibArgsMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), FibArgs.ParseFrom); + static readonly string ServiceName = "/math.Math"; - readonly static Method<DivArgs, DivReply> divMethod = new Method<DivArgs, DivReply>( + static readonly Marshaller<DivArgs> DivArgsMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), DivArgs.ParseFrom); + static readonly Marshaller<DivReply> DivReplyMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), DivReply.ParseFrom); + static readonly Marshaller<Num> NumMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), Num.ParseFrom); + static readonly Marshaller<FibArgs> FibArgsMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), FibArgs.ParseFrom); + + static readonly Method<DivArgs, DivReply> DivMethod = new Method<DivArgs, DivReply>( MethodType.Unary, - "/math.Math/Div", - divArgsMarshaller, - divReplyMarshaller - ); - readonly static Method<FibArgs, Num> fibMethod = new Method<FibArgs, Num>( + "Div", + DivArgsMarshaller, + DivReplyMarshaller); + + static readonly Method<FibArgs, Num> FibMethod = new Method<FibArgs, Num>( MethodType.ServerStreaming, - "/math.Math/Fib", - fibArgsMarshaller, - numMarshaller - ); - readonly static Method<Num, Num> sumMethod = new Method<Num, Num>( + "Fib", + FibArgsMarshaller, + NumMarshaller); + + static readonly Method<Num, Num> SumMethod = new Method<Num, Num>( MethodType.ClientStreaming, - "/math.Math/Sum", - numMarshaller, - numMarshaller - ); - readonly static Method<DivArgs, DivReply> divManyMethod = new Method<DivArgs, DivReply>( + "Sum", + NumMarshaller, + NumMarshaller); + + static readonly Method<DivArgs, DivReply> DivManyMethod = new Method<DivArgs, DivReply>( MethodType.DuplexStreaming, - "/math.Math/DivMany", - divArgsMarshaller, - divReplyMarshaller - ); + "DivMany", + DivArgsMarshaller, + DivReplyMarshaller); public interface IMathServiceClient { @@ -88,42 +89,43 @@ namespace math IObserver<DivArgs> DivMany(IObserver<DivReply> responseObserver, CancellationToken token = default(CancellationToken)); } - public class MathServiceClientStub : IMathServiceClient + public class MathServiceClientStub : AbstractStub<MathServiceClientStub, StubConfiguration>, IMathServiceClient { - readonly Channel channel; + public MathServiceClientStub(Channel channel) : this(channel, StubConfiguration.Default) + { + } - public MathServiceClientStub(Channel channel) + public MathServiceClientStub(Channel channel, StubConfiguration config) : base(channel, config) { - this.channel = channel; } public DivReply Div(DivArgs request, CancellationToken token = default(CancellationToken)) { - var call = new Grpc.Core.Call<DivArgs, DivReply>(divMethod, channel); + var call = CreateCall(ServiceName, DivMethod); return Calls.BlockingUnaryCall(call, request, token); } public Task<DivReply> DivAsync(DivArgs request, CancellationToken token = default(CancellationToken)) { - var call = new Grpc.Core.Call<DivArgs, DivReply>(divMethod, channel); + var call = CreateCall(ServiceName, DivMethod); return Calls.AsyncUnaryCall(call, request, token); } public void Fib(FibArgs request, IObserver<Num> responseObserver, CancellationToken token = default(CancellationToken)) { - var call = new Grpc.Core.Call<FibArgs, Num>(fibMethod, channel); + var call = CreateCall(ServiceName, FibMethod); Calls.AsyncServerStreamingCall(call, request, responseObserver, token); } public ClientStreamingAsyncResult<Num, Num> Sum(CancellationToken token = default(CancellationToken)) { - var call = new Grpc.Core.Call<Num, Num>(sumMethod, channel); + var call = CreateCall(ServiceName, SumMethod); return Calls.AsyncClientStreamingCall(call, token); } public IObserver<DivArgs> DivMany(IObserver<DivReply> responseObserver, CancellationToken token = default(CancellationToken)) { - var call = new Grpc.Core.Call<DivArgs, DivReply>(divManyMethod, channel); + var call = CreateCall(ServiceName, DivManyMethod); return Calls.DuplexStreamingCall(call, responseObserver, token); } } @@ -142,16 +144,21 @@ namespace math public static ServerServiceDefinition BindService(IMathService serviceImpl) { - return ServerServiceDefinition.CreateBuilder("/math.Math/") - .AddMethod(divMethod, serviceImpl.Div) - .AddMethod(fibMethod, serviceImpl.Fib) - .AddMethod(sumMethod, serviceImpl.Sum) - .AddMethod(divManyMethod, serviceImpl.DivMany).Build(); + return ServerServiceDefinition.CreateBuilder(ServiceName) + .AddMethod(DivMethod, serviceImpl.Div) + .AddMethod(FibMethod, serviceImpl.Fib) + .AddMethod(SumMethod, serviceImpl.Sum) + .AddMethod(DivManyMethod, serviceImpl.DivMany).Build(); } public static IMathServiceClient NewStub(Channel channel) { return new MathServiceClientStub(channel); } + + public static IMathServiceClient NewStub(Channel channel, StubConfiguration config) + { + return new MathServiceClientStub(channel, config); + } } } diff --git a/src/csharp/Grpc.Examples/MathServiceImpl.cs b/src/csharp/Grpc.Examples/MathServiceImpl.cs index 76a08ce518..0b2357e0fa 100644 --- a/src/csharp/Grpc.Examples/MathServiceImpl.cs +++ b/src/csharp/Grpc.Examples/MathServiceImpl.cs @@ -73,8 +73,8 @@ namespace math public IObserver<Num> Sum(IObserver<Num> responseObserver) { var recorder = new RecordingObserver<Num>(); - Task.Factory.StartNew(() => { - + Task.Factory.StartNew(() => + { List<Num> inputs = recorder.ToList().Result; long sum = 0; @@ -104,7 +104,7 @@ namespace math static IEnumerable<Num> FibInternal(long n) { long a = 1; - yield return new Num.Builder { Num_=a }.Build(); + yield return new Num.Builder { Num_ = a }.Build(); long b = 1; for (long i = 0; i < n - 1; i++) @@ -112,12 +112,12 @@ namespace math long temp = a; a = b; b = temp + b; - yield return new Num.Builder { Num_=a }.Build(); + yield return new Num.Builder { Num_ = a }.Build(); } } - private class DivObserver : IObserver<DivArgs> { - + private class DivObserver : IObserver<DivArgs> + { readonly IObserver<DivReply> responseObserver; public DivObserver(IObserver<DivReply> responseObserver) @@ -142,4 +142,3 @@ namespace math } } } - diff --git a/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs index 7603db7ffd..b55d24166c 100644 --- a/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.Examples/Properties/AssemblyInfo.cs @@ -1,22 +1,12 @@ using System.Reflection; using System.Runtime.CompilerServices; -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. -[assembly: AssemblyTitle ("Grpc.Examples")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("Google Inc. All rights reserved.")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. -[assembly: AssemblyVersion ("0.1.*")] -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - +[assembly: AssemblyTitle("Grpc.Examples")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("0.1.*")] diff --git a/src/csharp/Grpc.Examples/Settings.StyleCop b/src/csharp/Grpc.Examples/Settings.StyleCop new file mode 100644 index 0000000000..e9b6e7172a --- /dev/null +++ b/src/csharp/Grpc.Examples/Settings.StyleCop @@ -0,0 +1,10 @@ +<StyleCopSettings Version="105"> + <SourceFileList> + <SourceFile>Math.cs</SourceFile> + <Settings> + <GlobalSettings> + <BooleanProperty Name="RulesEnabledByDefault">False</BooleanProperty> + </GlobalSettings> + </Settings> + </SourceFileList> +</StyleCopSettings>
\ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs index d1f9e8560d..c93dd1eb2f 100644 --- a/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs @@ -1,8 +1,6 @@ using System.Reflection; using System.Runtime.CompilerServices; -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. [assembly: AssemblyTitle("Grpc.IntegrationTesting.Client")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] @@ -11,12 +9,4 @@ using System.Runtime.CompilerServices; [assembly: AssemblyCopyright("Google Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. [assembly: AssemblyVersion("0.1.*")] -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs index 4ef93f328d..f3def1aea4 100644 --- a/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs @@ -1,8 +1,6 @@ using System.Reflection; using System.Runtime.CompilerServices; -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. [assembly: AssemblyTitle("Grpc.IntegrationTesting.Server")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] @@ -11,12 +9,4 @@ using System.Runtime.CompilerServices; [assembly: AssemblyCopyright("Google Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. [assembly: AssemblyVersion("0.1.*")] -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj index 8f7a17efcb..c3e5f03074 100644 --- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj +++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj @@ -39,6 +39,9 @@ <Reference Include="Google.ProtocolBuffers"> <HintPath>..\packages\Google.ProtocolBuffers.2.4.1.521\lib\net40\Google.ProtocolBuffers.dll</HintPath> </Reference> + <Reference Include="System.Collections.Immutable"> + <HintPath>..\packages\System.Collections.Immutable.1.1.34-rc\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath> + </Reference> </ItemGroup> <ItemGroup> <Compile Include="Properties\AssemblyInfo.cs" /> @@ -49,6 +52,7 @@ <Compile Include="TestServiceImpl.cs" /> <Compile Include="InteropServer.cs" /> <Compile Include="InteropClient.cs" /> + <Compile Include="TestCredentials.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup> @@ -75,8 +79,5 @@ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup> - <ItemGroup> - <Folder Include="proto\" /> - <Folder Include="data\" /> - </ItemGroup> -</Project> + <ItemGroup /> +</Project>
\ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs index 30301f165b..6b92d3c660 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs @@ -38,10 +38,10 @@ using System.IO; using System.Text.RegularExpressions; using System.Threading.Tasks; using Google.ProtocolBuffers; +using grpc.testing; using Grpc.Core; using Grpc.Core.Utils; using NUnit.Framework; -using grpc.testing; namespace Grpc.IntegrationTesting { @@ -50,8 +50,8 @@ namespace Grpc.IntegrationTesting private class ClientOptions { public bool help; - public string serverHost= "127.0.0.1"; - public string serverHostOverride = "foo.test.google.fr"; + public string serverHost = "127.0.0.1"; + public string serverHostOverride = TestCredentials.DefaultHostOverride; public int? serverPort; public string testCase = "large_unary"; public bool useTls; @@ -103,22 +103,13 @@ namespace Grpc.IntegrationTesting Credentials credentials = null; if (options.useTls) { - string caPath = "data/ca.pem"; // Default testing CA - if (!options.useTestCa) - { - caPath = Environment.GetEnvironmentVariable("SSL_CERT_FILE"); - if (string.IsNullOrEmpty(caPath)) - { - throw new ArgumentException("CA path environment variable is not set."); - } - } - credentials = new SslCredentials(File.ReadAllText(caPath)); + credentials = TestCredentials.CreateTestClientCredentials(options.useTestCa); } ChannelArgs channelArgs = null; if (!string.IsNullOrEmpty(options.serverHostOverride)) { - channelArgs = ChannelArgs.NewBuilder() + channelArgs = ChannelArgs.CreateBuilder() .AddString(ChannelArgs.SslTargetNameOverrideKey, options.serverHostOverride).Build(); } @@ -189,7 +180,7 @@ namespace Grpc.IntegrationTesting { Console.WriteLine("running client_streaming"); - var bodySizes = new List<int>{27182, 8, 1828, 45904}; + var bodySizes = new List<int> { 27182, 8, 1828, 45904 }; var context = client.StreamingInputCall(); foreach (var size in bodySizes) @@ -208,7 +199,7 @@ namespace Grpc.IntegrationTesting { 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) @@ -265,7 +256,6 @@ namespace Grpc.IntegrationTesting Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); Assert.AreEqual(2653, response.Payload.Body.Length); - inputs.OnNext(StreamingOutputCallRequest.CreateBuilder() .SetResponseType(PayloadType.COMPRESSABLE) .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(58979)) @@ -301,17 +291,18 @@ namespace Grpc.IntegrationTesting public static void RunBenchmarkEmptyUnary(TestServiceGrpc.ITestServiceClient client) { BenchmarkUtil.RunBenchmark(10000, 10000, - () => { client.EmptyCall(Empty.DefaultInstance);}); + () => { client.EmptyCall(Empty.DefaultInstance); }); } - private static Payload CreateZerosPayload(int size) { + private static Payload CreateZerosPayload(int size) + { return Payload.CreateBuilder().SetBody(ByteString.CopyFrom(new byte[size])).Build(); } private static ClientOptions ParseArguments(string[] args) { var options = new ClientOptions(); - foreach(string arg in args) + foreach (string arg in args) { ParseArgument(arg, options); if (options.help) diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs index 4bb0b9ee51..1e76d3df21 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs @@ -35,10 +35,10 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using grpc.testing; using Grpc.Core; using Grpc.Core.Utils; using NUnit.Framework; -using grpc.testing; namespace Grpc.IntegrationTesting { @@ -59,9 +59,13 @@ namespace Grpc.IntegrationTesting server = new Server(); server.AddServiceDefinition(TestServiceGrpc.BindService(new TestServiceImpl())); - int port = server.AddPort(host + ":0"); + int port = server.AddListeningPort(host + ":0", TestCredentials.CreateTestServerCredentials()); server.Start(); - channel = new Channel(host + ":" + port); + + var channelArgs = ChannelArgs.CreateBuilder() + .AddString(ChannelArgs.SslTargetNameOverrideKey, TestCredentials.DefaultHostOverride).Build(); + + channel = new Channel(host + ":" + port, TestCredentials.CreateTestClientCredentials(true), channelArgs); client = TestServiceGrpc.NewStub(channel); } @@ -113,7 +117,5 @@ namespace Grpc.IntegrationTesting // TODO: add cancel_after_begin // TODO: add cancel_after_first_response - } } - diff --git a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs index a25d3b3530..ad5200774f 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs @@ -34,13 +34,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Text.RegularExpressions; using System.Threading.Tasks; using Google.ProtocolBuffers; +using grpc.testing; using Grpc.Core; using Grpc.Core.Utils; using NUnit.Framework; -using grpc.testing; namespace Grpc.IntegrationTesting { @@ -49,7 +50,7 @@ namespace Grpc.IntegrationTesting private class ServerOptions { public bool help; - public int? port; + public int? port = 8070; public bool useTls; } @@ -93,7 +94,14 @@ namespace Grpc.IntegrationTesting server.AddServiceDefinition(TestServiceGrpc.BindService(new TestServiceImpl())); string addr = "0.0.0.0:" + options.port; - server.AddPort(addr); + if (options.useTls) + { + server.AddListeningPort(addr, TestCredentials.CreateTestServerCredentials()); + } + else + { + server.AddListeningPort(addr); + } Console.WriteLine("Running server on " + addr); server.Start(); @@ -105,7 +113,7 @@ namespace Grpc.IntegrationTesting private static ServerOptions ParseArguments(string[] args) { var options = new ServerOptions(); - foreach(string arg in args) + foreach (string arg in args) { ParseArgument(arg, options); if (options.help) diff --git a/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs index f633c19132..f09a448e9e 100644 --- a/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs +++ b/src/csharp/Grpc.IntegrationTesting/Properties/AssemblyInfo.cs @@ -1,8 +1,6 @@ using System.Reflection; using System.Runtime.CompilerServices; -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. [assembly: AssemblyTitle("Grpc.IntegrationTesting")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] @@ -11,12 +9,4 @@ using System.Runtime.CompilerServices; [assembly: AssemblyCopyright("Google Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. [assembly: AssemblyVersion("0.1.*")] -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/src/csharp/Grpc.IntegrationTesting/Settings.StyleCop b/src/csharp/Grpc.IntegrationTesting/Settings.StyleCop new file mode 100644 index 0000000000..fb99cd4af1 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/Settings.StyleCop @@ -0,0 +1,11 @@ +<StyleCopSettings Version="105"> + <SourceFileList> + <SourceFile>Messages.cs</SourceFile> + <SourceFile>Empty.cs</SourceFile> + <Settings> + <GlobalSettings> + <BooleanProperty Name="RulesEnabledByDefault">False</BooleanProperty> + </GlobalSettings> + </Settings> + </SourceFileList> +</StyleCopSettings>
\ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting/TestCredentials.cs b/src/csharp/Grpc.IntegrationTesting/TestCredentials.cs new file mode 100644 index 0000000000..401c50b1ae --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/TestCredentials.cs @@ -0,0 +1,84 @@ +#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.Collections.Immutable; +using System.Diagnostics; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Google.ProtocolBuffers; +using grpc.testing; +using Grpc.Core; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.IntegrationTesting +{ + /// <summary> + /// SSL Credentials for testing. + /// </summary> + public static class TestCredentials + { + public const string DefaultHostOverride = "foo.test.google.fr"; + + public const string ClientCertAuthorityPath = "data/ca.pem"; + public const string ClientCertAuthorityEnvName = "SSL_CERT_FILE"; + + public const string ServerCertChainPath = "data/server1.pem"; + public const string ServerPrivateKeyPath = "data/server1.key"; + + public static SslCredentials CreateTestClientCredentials(bool useTestCa) + { + string caPath = ClientCertAuthorityPath; + if (!useTestCa) + { + caPath = Environment.GetEnvironmentVariable(ClientCertAuthorityEnvName); + if (string.IsNullOrEmpty(caPath)) + { + throw new ArgumentException("CA path environment variable is not set."); + } + } + return new SslCredentials(File.ReadAllText(caPath)); + } + + public static SslServerCredentials CreateTestServerCredentials() + { + var keyCertPair = new KeyCertificatePair( + File.ReadAllText(ServerCertChainPath), + File.ReadAllText(ServerPrivateKeyPath)); + return new SslServerCredentials(ImmutableList.Create(keyCertPair)); + } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting/TestServiceGrpc.cs b/src/csharp/Grpc.IntegrationTesting/TestServiceGrpc.cs index b71704bcc7..f63e0361a4 100644 --- a/src/csharp/Grpc.IntegrationTesting/TestServiceGrpc.cs +++ b/src/csharp/Grpc.IntegrationTesting/TestServiceGrpc.cs @@ -44,50 +44,51 @@ namespace grpc.testing /// </summary> public class TestServiceGrpc { - readonly static Marshaller<Empty> emptyMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), Empty.ParseFrom); - readonly static Marshaller<SimpleRequest> simpleRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), SimpleRequest.ParseFrom); - readonly static Marshaller<SimpleResponse> simpleResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), SimpleResponse.ParseFrom); - readonly static Marshaller<StreamingOutputCallRequest> streamingOutputCallRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingOutputCallRequest.ParseFrom); - readonly static Marshaller<StreamingOutputCallResponse> streamingOutputCallResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingOutputCallResponse.ParseFrom); - readonly static Marshaller<StreamingInputCallRequest> streamingInputCallRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingInputCallRequest.ParseFrom); - readonly static Marshaller<StreamingInputCallResponse> streamingInputCallResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingInputCallResponse.ParseFrom); - - readonly static Method<Empty, Empty> emptyCallMethod = new Method<Empty, Empty>( + static readonly string ServiceName = "/grpc.testing.TestService"; + + static readonly Marshaller<Empty> EmptyMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), Empty.ParseFrom); + static readonly Marshaller<SimpleRequest> SimpleRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), SimpleRequest.ParseFrom); + static readonly Marshaller<SimpleResponse> SimpleResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), SimpleResponse.ParseFrom); + static readonly Marshaller<StreamingOutputCallRequest> StreamingOutputCallRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingOutputCallRequest.ParseFrom); + static readonly Marshaller<StreamingOutputCallResponse> StreamingOutputCallResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingOutputCallResponse.ParseFrom); + static readonly Marshaller<StreamingInputCallRequest> StreamingInputCallRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingInputCallRequest.ParseFrom); + static readonly Marshaller<StreamingInputCallResponse> StreamingInputCallResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingInputCallResponse.ParseFrom); + + static readonly Method<Empty, Empty> EmptyCallMethod = new Method<Empty, Empty>( MethodType.Unary, - "/grpc.testing.TestService/EmptyCall", - emptyMarshaller, - emptyMarshaller - ); - readonly static Method<SimpleRequest, SimpleResponse> unaryCallMethod = new Method<SimpleRequest, SimpleResponse>( + "EmptyCall", + EmptyMarshaller, + EmptyMarshaller); + + static readonly Method<SimpleRequest, SimpleResponse> UnaryCallMethod = new Method<SimpleRequest, SimpleResponse>( MethodType.Unary, - "/grpc.testing.TestService/UnaryCall", - simpleRequestMarshaller, - simpleResponseMarshaller - ); - readonly static Method<StreamingOutputCallRequest, StreamingOutputCallResponse> streamingOutputCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>( + "UnaryCall", + SimpleRequestMarshaller, + SimpleResponseMarshaller); + + static readonly Method<StreamingOutputCallRequest, StreamingOutputCallResponse> StreamingOutputCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>( MethodType.ServerStreaming, - "/grpc.testing.TestService/StreamingOutputCall", - streamingOutputCallRequestMarshaller, - streamingOutputCallResponseMarshaller - ); - readonly static Method<StreamingInputCallRequest, StreamingInputCallResponse> streamingInputCallMethod = new Method<StreamingInputCallRequest, StreamingInputCallResponse>( + "StreamingOutputCall", + StreamingOutputCallRequestMarshaller, + StreamingOutputCallResponseMarshaller); + + static readonly Method<StreamingInputCallRequest, StreamingInputCallResponse> StreamingInputCallMethod = new Method<StreamingInputCallRequest, StreamingInputCallResponse>( MethodType.ClientStreaming, - "/grpc.testing.TestService/StreamingInputCall", - streamingInputCallRequestMarshaller, - streamingInputCallResponseMarshaller - ); - readonly static Method<StreamingOutputCallRequest, StreamingOutputCallResponse> fullDuplexCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>( + "StreamingInputCall", + StreamingInputCallRequestMarshaller, + StreamingInputCallResponseMarshaller); + + static readonly Method<StreamingOutputCallRequest, StreamingOutputCallResponse> FullDuplexCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>( MethodType.DuplexStreaming, - "/grpc.testing.TestService/FullDuplexCall", - streamingOutputCallRequestMarshaller, - streamingOutputCallResponseMarshaller - ); - readonly static Method<StreamingOutputCallRequest, StreamingOutputCallResponse> halfDuplexCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>( + "FullDuplexCall", + StreamingOutputCallRequestMarshaller, + StreamingOutputCallResponseMarshaller); + + static readonly Method<StreamingOutputCallRequest, StreamingOutputCallResponse> HalfDuplexCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>( MethodType.DuplexStreaming, - "/grpc.testing.TestService/HalfDuplexCall", - streamingOutputCallRequestMarshaller, - streamingOutputCallResponseMarshaller - ); + "HalfDuplexCall", + StreamingOutputCallRequestMarshaller, + StreamingOutputCallResponseMarshaller); public interface ITestServiceClient { @@ -108,60 +109,61 @@ namespace grpc.testing IObserver<StreamingOutputCallRequest> HalfDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)); } - public class TestServiceClientStub : ITestServiceClient + public class TestServiceClientStub : AbstractStub<TestServiceClientStub, StubConfiguration>, ITestServiceClient { - readonly Channel channel; + public TestServiceClientStub(Channel channel) : base(channel, StubConfiguration.Default) + { + } - public TestServiceClientStub(Channel channel) + public TestServiceClientStub(Channel channel, StubConfiguration config) : base(channel, config) { - this.channel = channel; } public Empty EmptyCall(Empty request, CancellationToken token = default(CancellationToken)) { - var call = new Grpc.Core.Call<Empty, Empty>(emptyCallMethod, channel); + var call = CreateCall(ServiceName, EmptyCallMethod); return Calls.BlockingUnaryCall(call, request, token); } public Task<Empty> EmptyCallAsync(Empty request, CancellationToken token = default(CancellationToken)) { - var call = new Grpc.Core.Call<Empty, Empty>(emptyCallMethod, channel); + var call = CreateCall(ServiceName, EmptyCallMethod); return Calls.AsyncUnaryCall(call, request, token); } public SimpleResponse UnaryCall(SimpleRequest request, CancellationToken token = default(CancellationToken)) { - var call = new Grpc.Core.Call<SimpleRequest, SimpleResponse>(unaryCallMethod, channel); + var call = CreateCall(ServiceName, UnaryCallMethod); return Calls.BlockingUnaryCall(call, request, token); } public Task<SimpleResponse> UnaryCallAsync(SimpleRequest request, CancellationToken token = default(CancellationToken)) { - var call = new Grpc.Core.Call<SimpleRequest, SimpleResponse>(unaryCallMethod, channel); + var call = CreateCall(ServiceName, UnaryCallMethod); return Calls.AsyncUnaryCall(call, request, token); } - public void StreamingOutputCall(StreamingOutputCallRequest request, IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)) { - var call = new Grpc.Core.Call<StreamingOutputCallRequest, StreamingOutputCallResponse>(streamingOutputCallMethod, channel); + public void StreamingOutputCall(StreamingOutputCallRequest request, IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)) + { + var call = CreateCall(ServiceName, StreamingOutputCallMethod); Calls.AsyncServerStreamingCall(call, request, responseObserver, token); } public ClientStreamingAsyncResult<StreamingInputCallRequest, StreamingInputCallResponse> StreamingInputCall(CancellationToken token = default(CancellationToken)) { - var call = new Grpc.Core.Call<StreamingInputCallRequest, StreamingInputCallResponse>(streamingInputCallMethod, channel); + var call = CreateCall(ServiceName, StreamingInputCallMethod); return Calls.AsyncClientStreamingCall(call, token); } public IObserver<StreamingOutputCallRequest> FullDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)) { - var call = new Grpc.Core.Call<StreamingOutputCallRequest, StreamingOutputCallResponse>(fullDuplexCallMethod, channel); + var call = CreateCall(ServiceName, FullDuplexCallMethod); return Calls.DuplexStreamingCall(call, responseObserver, token); } - public IObserver<StreamingOutputCallRequest> HalfDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)) { - var call = new Grpc.Core.Call<StreamingOutputCallRequest, StreamingOutputCallResponse>(halfDuplexCallMethod, channel); + var call = CreateCall(ServiceName, HalfDuplexCallMethod); return Calls.DuplexStreamingCall(call, responseObserver, token); } } @@ -184,13 +186,13 @@ namespace grpc.testing public static ServerServiceDefinition BindService(ITestService serviceImpl) { - return ServerServiceDefinition.CreateBuilder("/grpc.testing.TestService/") - .AddMethod(emptyCallMethod, serviceImpl.EmptyCall) - .AddMethod(unaryCallMethod, serviceImpl.UnaryCall) - .AddMethod(streamingOutputCallMethod, serviceImpl.StreamingOutputCall) - .AddMethod(streamingInputCallMethod, serviceImpl.StreamingInputCall) - .AddMethod(fullDuplexCallMethod, serviceImpl.FullDuplexCall) - .AddMethod(halfDuplexCallMethod, serviceImpl.HalfDuplexCall) + return ServerServiceDefinition.CreateBuilder(ServiceName) + .AddMethod(EmptyCallMethod, serviceImpl.EmptyCall) + .AddMethod(UnaryCallMethod, serviceImpl.UnaryCall) + .AddMethod(StreamingOutputCallMethod, serviceImpl.StreamingOutputCall) + .AddMethod(StreamingInputCallMethod, serviceImpl.StreamingInputCall) + .AddMethod(FullDuplexCallMethod, serviceImpl.FullDuplexCall) + .AddMethod(HalfDuplexCallMethod, serviceImpl.HalfDuplexCall) .Build(); } diff --git a/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs b/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs index 176843b130..661b31b0ee 100644 --- a/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs +++ b/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs @@ -55,14 +55,14 @@ namespace grpc.testing { var response = SimpleResponse.CreateBuilder() .SetPayload(CreateZerosPayload(request.ResponseSize)).Build(); - //TODO: check we support ReponseType + // TODO: check we support ReponseType responseObserver.OnNext(response); responseObserver.OnCompleted(); } public void StreamingOutputCall(StreamingOutputCallRequest request, IObserver<StreamingOutputCallResponse> responseObserver) { - foreach(var responseParam in request.ResponseParametersList) + foreach (var responseParam in request.ResponseParametersList) { var response = StreamingOutputCallResponse.CreateBuilder() .SetPayload(CreateZerosPayload(responseParam.Size)).Build(); @@ -74,9 +74,10 @@ namespace grpc.testing public IObserver<StreamingInputCallRequest> StreamingInputCall(IObserver<StreamingInputCallResponse> responseObserver) { var recorder = new RecordingObserver<StreamingInputCallRequest>(); - Task.Run(() => { + Task.Run(() => + { int sum = 0; - foreach(var req in recorder.ToList().Result) + foreach (var req in recorder.ToList().Result) { sum += req.Payload.Body.Length; } @@ -98,8 +99,8 @@ namespace grpc.testing throw new NotImplementedException(); } - private class FullDuplexObserver : IObserver<StreamingOutputCallRequest> { - + private class FullDuplexObserver : IObserver<StreamingOutputCallRequest> + { readonly IObserver<StreamingOutputCallResponse> responseObserver; public FullDuplexObserver(IObserver<StreamingOutputCallResponse> responseObserver) @@ -119,22 +120,18 @@ namespace grpc.testing public void OnNext(StreamingOutputCallRequest value) { - // TODO: this is not in order!!! - //Task.Factory.StartNew(() => { - - foreach(var responseParam in value.ResponseParametersList) - { - var response = StreamingOutputCallResponse.CreateBuilder() - .SetPayload(CreateZerosPayload(responseParam.Size)).Build(); - responseObserver.OnNext(response); - } - //}); + foreach (var responseParam in value.ResponseParametersList) + { + var response = StreamingOutputCallResponse.CreateBuilder() + .SetPayload(CreateZerosPayload(responseParam.Size)).Build(); + responseObserver.OnNext(response); + } } } - private static Payload CreateZerosPayload(int size) { + private static Payload CreateZerosPayload(int size) + { return Payload.CreateBuilder().SetBody(ByteString.CopyFrom(new byte[size])).Build(); } } } - diff --git a/src/csharp/Grpc.IntegrationTesting/packages.config b/src/csharp/Grpc.IntegrationTesting/packages.config index 51c17bcd5e..157c264eac 100644 --- a/src/csharp/Grpc.IntegrationTesting/packages.config +++ b/src/csharp/Grpc.IntegrationTesting/packages.config @@ -2,4 +2,5 @@ <packages> <package id="Google.ProtocolBuffers" version="2.4.1.521" targetFramework="net45" /> <package id="NUnit" version="2.6.4" targetFramework="net45" /> + <package id="System.Collections.Immutable" version="1.1.34-rc" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/src/csharp/Settings.StyleCop b/src/csharp/Settings.StyleCop new file mode 100644 index 0000000000..2ecf22f69e --- /dev/null +++ b/src/csharp/Settings.StyleCop @@ -0,0 +1,509 @@ +<StyleCopSettings Version="105"> + <Analyzers> + <Analyzer AnalyzerId="StyleCop.CSharp.DocumentationRules"> + <Rules> + <Rule Name="ElementsMustBeDocumented"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="PartialElementsMustBeDocumented"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="EnumerationItemsMustBeDocumented"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="DocumentationMustContainValidXml"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ElementDocumentationMustHaveSummary"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="PartialElementDocumentationMustHaveSummary"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ElementDocumentationMustHaveSummaryText"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="PartialElementDocumentationMustHaveSummaryText"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ElementDocumentationMustNotHaveDefaultSummary"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ElementParametersMustBeDocumented"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ElementParameterDocumentationMustMatchElementParameters"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ElementParameterDocumentationMustDeclareParameterName"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ElementParameterDocumentationMustHaveText"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ElementReturnValueMustBeDocumented"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ElementReturnValueDocumentationMustHaveText"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="VoidReturnValueMustNotBeDocumented"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="GenericTypeParametersMustBeDocumented"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="GenericTypeParametersMustBeDocumentedPartialClass"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="GenericTypeParameterDocumentationMustMatchTypeParameters"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="GenericTypeParameterDocumentationMustDeclareParameterName"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="GenericTypeParameterDocumentationMustHaveText"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="PropertySummaryDocumentationMustMatchAccessors"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="PropertySummaryDocumentationMustOmitSetAccessorWithRestrictedAccess"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ElementDocumentationMustNotBeCopiedAndPasted"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="SingleLineCommentsMustNotUseDocumentationStyleSlashes"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="DocumentationTextMustNotBeEmpty"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="DocumentationTextMustContainWhitespace"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="DocumentationMustMeetCharacterPercentage"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ConstructorSummaryDocumentationMustBeginWithStandardText"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="DestructorSummaryDocumentationMustBeginWithStandardText"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="DocumentationHeadersMustNotContainBlankLines"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="IncludedDocumentationXPathDoesNotExist"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="IncludeNodeDoesNotContainValidFileAndPath"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="InheritDocMustBeUsedWithInheritingClass"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ElementDocumentationMustBeSpelledCorrectly"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="FileMustHaveHeader"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="FileHeaderMustShowCopyright"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="FileHeaderMustHaveCopyrightText"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="FileHeaderMustContainFileName"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="FileHeaderFileNameDocumentationMustMatchFileName"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="FileHeaderMustHaveValidCompanyText"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="FileHeaderFileNameDocumentationMustMatchTypeName"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + </Rules> + <AnalyzerSettings /> + </Analyzer> + <Analyzer AnalyzerId="StyleCop.CSharp.MaintainabilityRules"> + <Rules> + <Rule Name="AccessModifierMustBeDeclared"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="FieldsMustBePrivate"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="CodeAnalysisSuppressionMustHaveJustification"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="DebugAssertMustProvideMessageText"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="DebugFailMustProvideMessageText"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="FileMayOnlyContainASingleClass"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="StatementMustNotUseUnnecessaryParenthesis"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ArithmeticExpressionsMustDeclarePrecedence"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ConditionalExpressionsMustDeclarePrecedence"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="RemoveDelegateParenthesisWhenPossible"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="AttributeConstructorMustNotUseUnnecessaryParenthesis"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="RemoveUnnecessaryCode"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + </Rules> + <AnalyzerSettings /> + </Analyzer> + <Analyzer AnalyzerId="StyleCop.CSharp.NamingRules"> + <Rules> + <Rule Name="NonPrivateReadonlyFieldsMustBeginWithUpperCaseLetter"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="FieldNamesMustNotUseHungarianNotation"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="FieldNamesMustBeginWithLowerCaseLetter"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="AccessibleFieldsMustBeginWithUpperCaseLetter"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="VariableNamesMustNotBePrefixed"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="FieldNamesMustNotBeginWithUnderscore"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="FieldNamesMustNotContainUnderscore"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ElementMustBeginWithUpperCaseLetter"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + </Rules> + <AnalyzerSettings /> + </Analyzer> + <Analyzer AnalyzerId="StyleCop.CSharp.OrderingRules"> + <Rules> + <Rule Name="UsingDirectivesMustBePlacedWithinNamespace"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ElementsMustAppearInTheCorrectOrder"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ElementsMustBeOrderedByAccess"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="StaticElementsMustAppearBeforeInstanceElements"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="PropertyAccessorsMustFollowOrder"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="EventAccessorsMustFollowOrder"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="UsingAliasDirectivesMustBePlacedAfterOtherUsingDirectives"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="UsingAliasDirectivesMustBeOrderedAlphabeticallyByAliasName"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + </Rules> + <AnalyzerSettings /> + </Analyzer> + <Analyzer AnalyzerId="StyleCop.CSharp.ReadabilityRules"> + <Rules> + <Rule Name="DoNotPrefixCallsWithBaseUnlessLocalImplementationExists"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="PrefixLocalCallsWithThis"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="PrefixCallsCorrectly"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="OpeningParenthesisMustBeOnDeclarationLine"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ClosingParenthesisMustBeOnLineOfLastParameter"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ClosingParenthesisMustBeOnLineOfOpeningParenthesis"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="CommaMustBeOnSameLineAsPreviousParameter"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ParameterListMustFollowDeclaration"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ParameterMustFollowComma"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="SplitParametersMustStartOnLineAfterDeclaration"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ParametersMustBeOnSameLineOrSeparateLines"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ParameterMustNotSpanMultipleLines"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="QueryClauseMustFollowPreviousClause"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="QueryClausesMustBeOnSeparateLinesOrAllOnOneLine"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="QueryClauseMustBeginOnNewLineWhenPreviousClauseSpansMultipleLines"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="QueryClausesSpanningMultipleLinesMustBeginOnOwnLine"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="CodeMustNotContainEmptyStatements"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="CodeMustNotContainMultipleStatementsOnOneLine"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="BlockStatementsMustNotContainEmbeddedComments"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="BlockStatementsMustNotContainEmbeddedRegions"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="UseStringEmptyForEmptyStrings"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + </Rules> + <AnalyzerSettings /> + </Analyzer> + <Analyzer AnalyzerId="StyleCop.CSharp.LayoutRules"> + <Rules> + <Rule Name="SingleLineCommentsMustNotBeFollowedByBlankLine"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ClosingCurlyBracketMustBeFollowedByBlankLine"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="ElementDocumentationHeaderMustBePrecededByBlankLine"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + <Rule Name="SingleLineCommentMustBePrecededByBlankLine"> + <RuleSettings> + <BooleanProperty Name="Enabled">False</BooleanProperty> + </RuleSettings> + </Rule> + </Rules> + <AnalyzerSettings /> + </Analyzer> + </Analyzers> +</StyleCopSettings>
\ No newline at end of file diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index e24438704c..9a1c908d11 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -102,34 +102,114 @@ grpcsharp_batch_context *grpcsharp_batch_context_create() { return ctx; } -/** - * Destroys metadata array including keys and values. +/* + * Destroys array->metadata. + * The array pointer itself is not freed. */ -void grpcsharp_metadata_array_destroy_recursive(grpc_metadata_array *array) { - if (!array->metadata) { +void grpcsharp_metadata_array_destroy_metadata_only( + grpc_metadata_array *array) { + gpr_free(array->metadata); +} + +/* + * Destroys keys, values and array->metadata. + * The array pointer itself is not freed. + */ +void grpcsharp_metadata_array_destroy_metadata_including_entries( + grpc_metadata_array *array) { + size_t i; + if (array->metadata) { + for (i = 0; i < array->count; i++) { + gpr_free((void *)array->metadata[i].key); + gpr_free((void *)array->metadata[i].value); + } + } + gpr_free(array->metadata); +} + +/* + * Fully destroys the metadata array. + */ +GPR_EXPORT void GPR_CALLTYPE +grpcsharp_metadata_array_destroy_full(grpc_metadata_array *array) { + if (!array) { return; } - /* TODO: destroy also keys and values */ - grpc_metadata_array_destroy(array); + grpcsharp_metadata_array_destroy_metadata_including_entries(array); + gpr_free(array); +} + +/* + * Creates an empty metadata array with given capacity. + * Array can later be destroyed by grpc_metadata_array_destroy_full. + */ +GPR_EXPORT grpc_metadata_array *GPR_CALLTYPE +grpcsharp_metadata_array_create(size_t capacity) { + grpc_metadata_array *array = + (grpc_metadata_array *)gpr_malloc(sizeof(grpc_metadata_array)); + grpc_metadata_array_init(array); + array->capacity = capacity; + array->count = 0; + if (capacity > 0) { + array->metadata = + (grpc_metadata *)gpr_malloc(sizeof(grpc_metadata) * capacity); + memset(array->metadata, 0, sizeof(grpc_metadata) * capacity); + } else { + array->metadata = NULL; + } + return array; +} + +GPR_EXPORT void GPR_CALLTYPE +grpcsharp_metadata_array_add(grpc_metadata_array *array, const char *key, + const char *value, size_t value_length) { + size_t i = array->count; + GPR_ASSERT(array->count < array->capacity); + array->metadata[i].key = gpr_strdup(key); + array->metadata[i].value = (char *)gpr_malloc(value_length); + memcpy((void *)array->metadata[i].value, value, value_length); + array->metadata[i].value_length = value_length; + array->count++; +} + +/* Move contents of metadata array */ +void grpcsharp_metadata_array_move(grpc_metadata_array *dest, + grpc_metadata_array *src) { + if (!src) { + dest->capacity = 0; + dest->count = 0; + dest->metadata = NULL; + return; + } + + dest->capacity = src->capacity; + dest->count = src->count; + dest->metadata = src->metadata; + + src->capacity = 0; + src->count = 0; + src->metadata = NULL; } void grpcsharp_batch_context_destroy(grpcsharp_batch_context *ctx) { if (!ctx) { return; } - grpcsharp_metadata_array_destroy_recursive(&(ctx->send_initial_metadata)); + grpcsharp_metadata_array_destroy_metadata_including_entries( + &(ctx->send_initial_metadata)); grpc_byte_buffer_destroy(ctx->send_message); - grpcsharp_metadata_array_destroy_recursive( + grpcsharp_metadata_array_destroy_metadata_including_entries( &(ctx->send_status_from_server.trailing_metadata)); gpr_free(ctx->send_status_from_server.status_details); - grpc_metadata_array_destroy(&(ctx->recv_initial_metadata)); + grpcsharp_metadata_array_destroy_metadata_only(&(ctx->recv_initial_metadata)); grpc_byte_buffer_destroy(ctx->recv_message); - grpc_metadata_array_destroy(&(ctx->recv_status_on_client.trailing_metadata)); + grpcsharp_metadata_array_destroy_metadata_only( + &(ctx->recv_status_on_client.trailing_metadata)); gpr_free((void *)ctx->recv_status_on_client.status_details); /* NOTE: ctx->server_rpc_new.call is not destroyed because callback handler is @@ -137,7 +217,8 @@ void grpcsharp_batch_context_destroy(grpcsharp_batch_context *ctx) { to take its ownership. */ grpc_call_details_destroy(&(ctx->server_rpc_new.call_details)); - grpc_metadata_array_destroy(&(ctx->server_rpc_new.request_metadata)); + grpcsharp_metadata_array_destroy_metadata_only( + &(ctx->server_rpc_new.request_metadata)); gpr_free(ctx); } @@ -346,17 +427,19 @@ grpcsharp_call_start_write_from_copied_buffer(grpc_call *call, GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_unary(grpc_call *call, callback_funcptr callback, - const char *send_buffer, size_t send_buffer_len) { + const char *send_buffer, size_t send_buffer_len, + grpc_metadata_array *initial_metadata) { /* TODO: don't use magic number */ grpc_op ops[6]; grpcsharp_batch_context *ctx = grpcsharp_batch_context_create(); ctx->callback = callback; - /* TODO: implement sending the metadata... */ ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; - /* ctx->send_initial_metadata is already zeroed out. */ - ops[0].data.send_initial_metadata.count = 0; - ops[0].data.send_initial_metadata.metadata = NULL; + grpcsharp_metadata_array_move(&(ctx->send_initial_metadata), + initial_metadata); + ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; + ops[0].data.send_initial_metadata.metadata = + ctx->send_initial_metadata.metadata; ops[1].op = GRPC_OP_SEND_MESSAGE; ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len); @@ -389,9 +472,11 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_call_blocking_unary(grpc_call *call, grpc_completion_queue *dedicated_cq, callback_funcptr callback, - const char *send_buffer, size_t send_buffer_len) { + const char *send_buffer, size_t send_buffer_len, + grpc_metadata_array *initial_metadata) { GPR_ASSERT(grpcsharp_call_start_unary(call, callback, send_buffer, - send_buffer_len) == GRPC_CALL_OK); + send_buffer_len, + initial_metadata) == GRPC_CALL_OK); /* TODO: we would like to use pluck, but we don't know the tag */ GPR_ASSERT(grpcsharp_completion_queue_next_with_callback(dedicated_cq) == @@ -403,17 +488,19 @@ grpcsharp_call_blocking_unary(grpc_call *call, GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_client_streaming(grpc_call *call, - callback_funcptr callback) { + callback_funcptr callback, + grpc_metadata_array *initial_metadata) { /* TODO: don't use magic number */ grpc_op ops[4]; grpcsharp_batch_context *ctx = grpcsharp_batch_context_create(); ctx->callback = callback; - /* TODO: implement sending the metadata... */ ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; - /* ctx->send_initial_metadata is already zeroed out. */ - ops[0].data.send_initial_metadata.count = 0; - ops[0].data.send_initial_metadata.metadata = NULL; + grpcsharp_metadata_array_move(&(ctx->send_initial_metadata), + initial_metadata); + ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; + ops[0].data.send_initial_metadata.metadata = + ctx->send_initial_metadata.metadata; ops[1].op = GRPC_OP_RECV_INITIAL_METADATA; ops[1].data.recv_initial_metadata = &(ctx->recv_initial_metadata); @@ -435,21 +522,20 @@ grpcsharp_call_start_client_streaming(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_start_server_streaming(grpc_call *call, - callback_funcptr callback, - const char *send_buffer, - size_t send_buffer_len) { +GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_server_streaming( + grpc_call *call, callback_funcptr callback, const char *send_buffer, + size_t send_buffer_len, grpc_metadata_array *initial_metadata) { /* TODO: don't use magic number */ grpc_op ops[5]; grpcsharp_batch_context *ctx = grpcsharp_batch_context_create(); ctx->callback = callback; - /* TODO: implement sending the metadata... */ ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; - /* ctx->send_initial_metadata is already zeroed out. */ - ops[0].data.send_initial_metadata.count = 0; - ops[0].data.send_initial_metadata.metadata = NULL; + grpcsharp_metadata_array_move(&(ctx->send_initial_metadata), + initial_metadata); + ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; + ops[0].data.send_initial_metadata.metadata = + ctx->send_initial_metadata.metadata; ops[1].op = GRPC_OP_SEND_MESSAGE; ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len); @@ -476,17 +562,19 @@ grpcsharp_call_start_server_streaming(grpc_call *call, GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_duplex_streaming(grpc_call *call, - callback_funcptr callback) { + callback_funcptr callback, + grpc_metadata_array *initial_metadata) { /* TODO: don't use magic number */ grpc_op ops[3]; grpcsharp_batch_context *ctx = grpcsharp_batch_context_create(); ctx->callback = callback; - /* TODO: implement sending the metadata... */ ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; - /* ctx->send_initial_metadata is already zeroed out. */ - ops[0].data.send_initial_metadata.count = 0; - ops[0].data.send_initial_metadata.metadata = NULL; + grpcsharp_metadata_array_move(&(ctx->send_initial_metadata), + initial_metadata); + ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; + ops[0].data.send_initial_metadata.metadata = + ctx->send_initial_metadata.metadata; ops[1].op = GRPC_OP_RECV_INITIAL_METADATA; ops[1].data.recv_initial_metadata = &(ctx->recv_initial_metadata); @@ -653,6 +741,41 @@ grpcsharp_secure_channel_create(grpc_credentials *creds, const char *target, return grpc_secure_channel_create(creds, target, args); } +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) { + size_t i; + grpc_server_credentials *creds; + grpc_ssl_pem_key_cert_pair *key_cert_pairs = + gpr_malloc(sizeof(grpc_ssl_pem_key_cert_pair) * num_key_cert_pairs); + memset(key_cert_pairs, 0, + sizeof(grpc_ssl_pem_key_cert_pair) * num_key_cert_pairs); + + for (i = 0; i < num_key_cert_pairs; i++) { + if (key_cert_pair_cert_chain_array[i] || + key_cert_pair_private_key_array[i]) { + key_cert_pairs[i].cert_chain = key_cert_pair_cert_chain_array[i]; + key_cert_pairs[i].private_key = key_cert_pair_private_key_array[i]; + } + } + creds = grpc_ssl_server_credentials_create(pem_root_certs, key_cert_pairs, + num_key_cert_pairs); + gpr_free(key_cert_pairs); + return creds; +} + +GPR_EXPORT void grpcsharp_server_credentials_release( + grpc_server_credentials *creds) { + grpc_server_credentials_release(creds); +} + +GPR_EXPORT gpr_int32 GPR_CALLTYPE +grpcsharp_server_add_secure_http2_port(grpc_server *server, const char *addr, + grpc_server_credentials *creds) { + return grpc_server_add_secure_http2_port(server, addr, creds); +} + /* Logging */ typedef void(GPR_CALLTYPE *grpcsharp_log_func)(const char *file, gpr_int32 line, |