diff options
author | 2015-03-17 09:35:19 -0700 | |
---|---|---|
committer | 2015-03-17 09:35:19 -0700 | |
commit | 09eaec027ab4b3d97cb239820af98fe0c460f287 (patch) | |
tree | 150db9cb6b7d20f3022735ad7876f32080b66bf4 /src/csharp/Grpc.Core | |
parent | 4708fbb3fb55afb6481c3737b2808c87b917a790 (diff) | |
parent | c0b3721d61fed71a3528cecfa6797cdc26722d3a (diff) |
Merge pull request #1053 from jtattermusch/csharp_metadata
Added support for sending metadata from clients.
Diffstat (limited to 'src/csharp/Grpc.Core')
-rw-r--r-- | src/csharp/Grpc.Core/Call.cs | 55 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Calls.cs | 30 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Grpc.Core.csproj | 7 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Internal/AsyncCall.cs | 37 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Internal/CallSafeHandle.cs | 33 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs | 72 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Marshaller.cs | 5 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Metadata.cs | 126 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/ServerServiceDefinition.cs | 15 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Stub/AbstractStub.cs | 73 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Stub/StubConfiguration.cs | 64 |
11 files changed, 442 insertions, 75 deletions
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/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/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; + } + } + } +} |