aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/csharp
diff options
context:
space:
mode:
authorGravatar Nicolas "Pixel" Noble <pixel@nobis-crew.org>2015-03-19 19:21:15 +0100
committerGravatar Nicolas "Pixel" Noble <pixel@nobis-crew.org>2015-03-19 19:21:15 +0100
commitf0863b02270f1b95d5c8b9f3f962959e4cbbdd42 (patch)
treee8a441a621d0addfa82a8fc3052080e78a012a2a /src/csharp
parentf358573091ef9c14c39ea56d9d9883410a533992 (diff)
parent43c5fe6825098b127e33159d3cf14ac937ce7db5 (diff)
Merge branch 'master' of github.com:google/grpc into freebsd
Conflicts: Makefile templates/Makefile.template
Diffstat (limited to 'src/csharp')
-rw-r--r--src/csharp/Grpc.Core.Tests/ClientServerTest.cs20
-rw-r--r--src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj4
-rw-r--r--src/csharp/Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs62
-rw-r--r--src/csharp/Grpc.Core.Tests/ServerTest.cs2
-rw-r--r--src/csharp/Grpc.Core/Call.cs55
-rw-r--r--src/csharp/Grpc.Core/Calls.cs30
-rw-r--r--src/csharp/Grpc.Core/Grpc.Core.csproj7
-rw-r--r--src/csharp/Grpc.Core/Internal/AsyncCall.cs37
-rw-r--r--src/csharp/Grpc.Core/Internal/CallSafeHandle.cs33
-rw-r--r--src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs72
-rw-r--r--src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs4
-rw-r--r--src/csharp/Grpc.Core/Marshaller.cs5
-rw-r--r--src/csharp/Grpc.Core/Metadata.cs126
-rw-r--r--src/csharp/Grpc.Core/Server.cs8
-rw-r--r--src/csharp/Grpc.Core/ServerServiceDefinition.cs15
-rw-r--r--src/csharp/Grpc.Core/Stub/AbstractStub.cs73
-rw-r--r--src/csharp/Grpc.Core/Stub/StubConfiguration.cs64
-rw-r--r--src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs11
-rw-r--r--src/csharp/Grpc.Examples/MathGrpc.cs36
-rw-r--r--src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj3
-rw-r--r--src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs2
-rw-r--r--src/csharp/Grpc.IntegrationTesting/InteropServer.cs4
-rw-r--r--src/csharp/Grpc.IntegrationTesting/TestServiceGrpc.cs41
-rw-r--r--src/csharp/ext/grpc_csharp_ext.c162
24 files changed, 708 insertions, 168 deletions
diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs
index e73159b350..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,14 +115,14 @@ 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
{
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/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/ServerTest.cs b/src/csharp/Grpc.Core.Tests/ServerTest.cs
index 12f914bfad..2a1855da67 100644
--- a/src/csharp/Grpc.Core.Tests/ServerTest.cs
+++ b/src/csharp/Grpc.Core.Tests/ServerTest.cs
@@ -47,7 +47,7 @@ namespace Grpc.Core.Tests
GrpcEnvironment.Initialize();
Server server = new Server();
- server.AddPort("localhost:0");
+ server.AddListeningPort("localhost:0");
server.Start();
server.ShutdownAsync().Wait();
diff --git a/src/csharp/Grpc.Core/Call.cs b/src/csharp/Grpc.Core/Call.cs
index d84d5940c2..fe5f40f5e9 100644
--- a/src/csharp/Grpc.Core/Call.cs
+++ b/src/csharp/Grpc.Core/Call.cs
@@ -33,65 +33,70 @@
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)
+ public Call(string serviceName, Method<TRequest, TResponse> method, Channel channel, Metadata headers)
{
- this.methodName = methodName;
- this.requestSerializer = requestSerializer;
- this.responseDeserializer = responseDeserializer;
- 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 Call(Method<TRequest, TResponse> method, Channel channel)
+ public Channel Channel
{
- this.methodName = method.Name;
- this.requestSerializer = method.RequestMarshaller.Serializer;
- this.responseDeserializer = method.ResponseMarshaller.Deserializer;
- this.channel = channel;
+ get
+ {
+ return this.channel;
+ }
}
- public Channel Channel
+ /// <summary>
+ /// Full methods name including the service name.
+ /// </summary>
+ public string Name
{
get
{
- return this.channel;
+ return name;
}
}
- public string MethodName
+ /// <summary>
+ /// Headers to send at the beginning of the call.
+ /// </summary>
+ public Metadata Headers
{
get
{
- return this.methodName;
+ return headers;
}
}
- public Func<TRequest, byte[]> RequestSerializer
+ public Marshaller<TRequest> RequestMarshaller
{
get
{
- return this.requestSerializer;
+ return requestMarshaller;
}
}
- public Func<byte[], TResponse> ResponseDeserializer
+ public Marshaller<TResponse> ResponseMarshaller
{
get
{
- return this.responseDeserializer;
+ return responseMarshaller;
}
}
}
diff --git a/src/csharp/Grpc.Core/Calls.cs b/src/csharp/Grpc.Core/Calls.cs
index cc1d67afca..280387b323 100644
--- a/src/csharp/Grpc.Core/Calls.cs
+++ b/src/csharp/Grpc.Core/Calls.cs
@@ -45,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);
}
@@ -80,10 +79,9 @@ 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);
}
diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj
index 29f1a0604a..78ba32b277 100644
--- a/src/csharp/Grpc.Core/Grpc.Core.csproj
+++ b/src/csharp/Grpc.Core/Grpc.Core.csproj
@@ -79,6 +79,10 @@
<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" />
@@ -96,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/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
index 04fc28d988..bc72cb78de 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
@@ -77,7 +77,7 @@ 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())
{
@@ -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();
}
diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
index a8cef4a68b..14add60c72 100644
--- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
@@ -57,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,
@@ -109,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)
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/ServerSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
index dc4781e796..a59da09822 100644
--- a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
@@ -80,12 +80,12 @@ namespace Grpc.Core.Internal
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 AddPort(string addr, ServerCredentialsSafeHandle credentials)
+ public int AddListeningPort(string addr, ServerCredentialsSafeHandle credentials)
{
return grpcsharp_server_add_secure_http2_port(this, addr, credentials);
}
diff --git a/src/csharp/Grpc.Core/Marshaller.cs b/src/csharp/Grpc.Core/Marshaller.cs
index e73e7b762e..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
{
@@ -45,8 +46,8 @@ namespace Grpc.Core
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
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/Server.cs b/src/csharp/Grpc.Core/Server.cs
index 2439cdb6dc..f086fa8beb 100644
--- a/src/csharp/Grpc.Core/Server.cs
+++ b/src/csharp/Grpc.Core/Server.cs
@@ -76,17 +76,17 @@ namespace Grpc.Core
}
// only call before Start()
- public int AddPort(string addr)
+ public int AddListeningPort(string addr)
{
- return handle.AddPort(addr);
+ return handle.AddListeningPort(addr);
}
// only call before Start()
- public int AddPort(string addr, ServerCredentials credentials)
+ public int AddListeningPort(string addr, ServerCredentials credentials)
{
using (var nativeCredentials = credentials.ToNativeCredentials())
{
- return handle.AddPort(addr, nativeCredentials);
+ return handle.AddListeningPort(addr, nativeCredentials);
}
}
diff --git a/src/csharp/Grpc.Core/ServerServiceDefinition.cs b/src/csharp/Grpc.Core/ServerServiceDefinition.cs
index 004415477c..f08c7d88f3 100644
--- a/src/csharp/Grpc.Core/ServerServiceDefinition.cs
+++ b/src/csharp/Grpc.Core/ServerServiceDefinition.cs
@@ -43,12 +43,10 @@ namespace Grpc.Core
/// </summary>
public class ServerServiceDefinition
{
- readonly string serviceName;
readonly ImmutableDictionary<string, IServerCallHandler> callHandlers;
- private ServerServiceDefinition(string serviceName, ImmutableDictionary<string, IServerCallHandler> callHandlers)
+ private ServerServiceDefinition(ImmutableDictionary<string, IServerCallHandler> callHandlers)
{
- this.serviceName = serviceName;
this.callHandlers = callHandlers;
}
@@ -79,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;
}
@@ -87,13 +85,18 @@ 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.ToImmutableDictionary());
+ return new ServerServiceDefinition(callHandlers.ToImmutableDictionary());
+ }
+
+ private string GetFullMethodName(string serviceName, string methodName)
+ {
+ return serviceName + "/" + methodName;
}
}
}
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.Examples.Tests/MathClientServerTests.cs b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs
index c86da65af4..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]
diff --git a/src/csharp/Grpc.Examples/MathGrpc.cs b/src/csharp/Grpc.Examples/MathGrpc.cs
index 33a9ca9287..24e6a1de8e 100644
--- a/src/csharp/Grpc.Examples/MathGrpc.cs
+++ b/src/csharp/Grpc.Examples/MathGrpc.cs
@@ -45,6 +45,8 @@ namespace math
/// </summary>
public class MathGrpc
{
+ static readonly string ServiceName = "/math.Math";
+
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);
@@ -52,25 +54,25 @@ namespace math
static readonly Method<DivArgs, DivReply> DivMethod = new Method<DivArgs, DivReply>(
MethodType.Unary,
- "/math.Math/Div",
+ "Div",
DivArgsMarshaller,
DivReplyMarshaller);
static readonly Method<FibArgs, Num> FibMethod = new Method<FibArgs, Num>(
MethodType.ServerStreaming,
- "/math.Math/Fib",
+ "Fib",
FibArgsMarshaller,
NumMarshaller);
static readonly Method<Num, Num> SumMethod = new Method<Num, Num>(
MethodType.ClientStreaming,
- "/math.Math/Sum",
+ "Sum",
NumMarshaller,
NumMarshaller);
static readonly Method<DivArgs, DivReply> DivManyMethod = new Method<DivArgs, DivReply>(
MethodType.DuplexStreaming,
- "/math.Math/DivMany",
+ "DivMany",
DivArgsMarshaller,
DivReplyMarshaller);
@@ -87,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);
}
}
@@ -141,7 +144,7 @@ namespace math
public static ServerServiceDefinition BindService(IMathService serviceImpl)
{
- return ServerServiceDefinition.CreateBuilder("/math.Math/")
+ return ServerServiceDefinition.CreateBuilder(ServiceName)
.AddMethod(DivMethod, serviceImpl.Div)
.AddMethod(FibMethod, serviceImpl.Fib)
.AddMethod(SumMethod, serviceImpl.Sum)
@@ -152,5 +155,10 @@ namespace math
{
return new MathServiceClientStub(channel);
}
+
+ public static IMathServiceClient NewStub(Channel channel, StubConfiguration config)
+ {
+ return new MathServiceClientStub(channel, config);
+ }
}
}
diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
index cfb258711a..c3e5f03074 100644
--- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
+++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
@@ -39,8 +39,7 @@
<Reference Include="Google.ProtocolBuffers">
<HintPath>..\packages\Google.ProtocolBuffers.2.4.1.521\lib\net40\Google.ProtocolBuffers.dll</HintPath>
</Reference>
- <Reference Include="System.Collections.Immutable, Version=1.1.34.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
+ <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>
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
index 814f6311f2..1e76d3df21 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
@@ -59,7 +59,7 @@ namespace Grpc.IntegrationTesting
server = new Server();
server.AddServiceDefinition(TestServiceGrpc.BindService(new TestServiceImpl()));
- int port = server.AddPort(host + ":0", TestCredentials.CreateTestServerCredentials());
+ int port = server.AddListeningPort(host + ":0", TestCredentials.CreateTestServerCredentials());
server.Start();
var channelArgs = ChannelArgs.CreateBuilder()
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs
index 5e580280b6..ad5200774f 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs
@@ -96,11 +96,11 @@ namespace Grpc.IntegrationTesting
string addr = "0.0.0.0:" + options.port;
if (options.useTls)
{
- server.AddPort(addr, TestCredentials.CreateTestServerCredentials());
+ server.AddListeningPort(addr, TestCredentials.CreateTestServerCredentials());
}
else
{
- server.AddPort(addr);
+ server.AddListeningPort(addr);
}
Console.WriteLine("Running server on " + addr);
server.Start();
diff --git a/src/csharp/Grpc.IntegrationTesting/TestServiceGrpc.cs b/src/csharp/Grpc.IntegrationTesting/TestServiceGrpc.cs
index 9b0251c3ca..f63e0361a4 100644
--- a/src/csharp/Grpc.IntegrationTesting/TestServiceGrpc.cs
+++ b/src/csharp/Grpc.IntegrationTesting/TestServiceGrpc.cs
@@ -44,6 +44,8 @@ namespace grpc.testing
/// </summary>
public class TestServiceGrpc
{
+ 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);
@@ -54,37 +56,37 @@ namespace grpc.testing
static readonly Method<Empty, Empty> EmptyCallMethod = new Method<Empty, Empty>(
MethodType.Unary,
- "/grpc.testing.TestService/EmptyCall",
+ "EmptyCall",
EmptyMarshaller,
EmptyMarshaller);
static readonly Method<SimpleRequest, SimpleResponse> UnaryCallMethod = new Method<SimpleRequest, SimpleResponse>(
MethodType.Unary,
- "/grpc.testing.TestService/UnaryCall",
+ "UnaryCall",
SimpleRequestMarshaller,
SimpleResponseMarshaller);
static readonly Method<StreamingOutputCallRequest, StreamingOutputCallResponse> StreamingOutputCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>(
MethodType.ServerStreaming,
- "/grpc.testing.TestService/StreamingOutputCall",
+ "StreamingOutputCall",
StreamingOutputCallRequestMarshaller,
StreamingOutputCallResponseMarshaller);
static readonly Method<StreamingInputCallRequest, StreamingInputCallResponse> StreamingInputCallMethod = new Method<StreamingInputCallRequest, StreamingInputCallResponse>(
MethodType.ClientStreaming,
- "/grpc.testing.TestService/StreamingInputCall",
+ "StreamingInputCall",
StreamingInputCallRequestMarshaller,
StreamingInputCallResponseMarshaller);
static readonly Method<StreamingOutputCallRequest, StreamingOutputCallResponse> FullDuplexCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>(
MethodType.DuplexStreaming,
- "/grpc.testing.TestService/FullDuplexCall",
+ "FullDuplexCall",
StreamingOutputCallRequestMarshaller,
StreamingOutputCallResponseMarshaller);
static readonly Method<StreamingOutputCallRequest, StreamingOutputCallResponse> HalfDuplexCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>(
MethodType.DuplexStreaming,
- "/grpc.testing.TestService/HalfDuplexCall",
+ "HalfDuplexCall",
StreamingOutputCallRequestMarshaller,
StreamingOutputCallResponseMarshaller);
@@ -107,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);
+ 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);
}
}
@@ -183,7 +186,7 @@ namespace grpc.testing
public static ServerServiceDefinition BindService(ITestService serviceImpl)
{
- return ServerServiceDefinition.CreateBuilder("/grpc.testing.TestService/")
+ return ServerServiceDefinition.CreateBuilder(ServiceName)
.AddMethod(EmptyCallMethod, serviceImpl.EmptyCall)
.AddMethod(UnaryCallMethod, serviceImpl.UnaryCall)
.AddMethod(StreamingOutputCallMethod, serviceImpl.StreamingOutputCall)
diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c
index 51abb632f7..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_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;
+ }
+ 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.
*/
-void grpcsharp_metadata_array_destroy_recursive(grpc_metadata_array *array) {
- if (!array->metadata) {
+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;
}
- /* TODO: destroy also keys and values */
- grpc_metadata_array_destroy(array);
+
+ 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);