aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/csharp/Grpc.Core
diff options
context:
space:
mode:
authorGravatar murgatroid99 <mlumish@google.com>2015-08-28 12:36:48 -0700
committerGravatar murgatroid99 <mlumish@google.com>2015-08-28 12:36:48 -0700
commit970842535d6abdaf6c5c040fb2b432c423200f26 (patch)
treefac649e584b56da9bff6c19e8d3a637ce55505a4 /src/csharp/Grpc.Core
parent4e53265f6470a8736967f9cbc2e0797ad04755ca (diff)
parent956e411e31c97836702aac5675e9f509b2231426 (diff)
Merge branch 'master' into node_method_name_conflicts
Diffstat (limited to 'src/csharp/Grpc.Core')
-rw-r--r--src/csharp/Grpc.Core/AsyncClientStreamingCall.cs17
-rw-r--r--src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs18
-rw-r--r--src/csharp/Grpc.Core/AsyncServerStreamingCall.cs17
-rw-r--r--src/csharp/Grpc.Core/AsyncUnaryCall.cs16
-rw-r--r--src/csharp/Grpc.Core/CallInvocationDetails.cs10
-rw-r--r--src/csharp/Grpc.Core/CallOptions.cs3
-rw-r--r--src/csharp/Grpc.Core/Calls.cs9
-rw-r--r--src/csharp/Grpc.Core/Channel.cs6
-rw-r--r--src/csharp/Grpc.Core/ChannelOptions.cs24
-rw-r--r--src/csharp/Grpc.Core/ClientBase.cs9
-rw-r--r--src/csharp/Grpc.Core/ContextPropagationToken.cs8
-rw-r--r--src/csharp/Grpc.Core/Grpc.Core.csproj1
-rw-r--r--src/csharp/Grpc.Core/GrpcEnvironment.cs9
-rw-r--r--src/csharp/Grpc.Core/IAsyncStreamReader.cs2
-rw-r--r--src/csharp/Grpc.Core/IAsyncStreamWriter.cs4
-rw-r--r--src/csharp/Grpc.Core/IClientStreamWriter.cs2
-rw-r--r--src/csharp/Grpc.Core/Internal/AsyncCall.cs160
-rw-r--r--src/csharp/Grpc.Core/Internal/AsyncCallBase.cs96
-rw-r--r--src/csharp/Grpc.Core/Internal/AsyncCallServer.cs13
-rw-r--r--src/csharp/Grpc.Core/Internal/CallSafeHandle.cs55
-rw-r--r--src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs2
-rw-r--r--src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs2
-rw-r--r--src/csharp/Grpc.Core/Internal/ClientResponseStream.cs8
-rw-r--r--src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs2
-rw-r--r--src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs2
-rw-r--r--src/csharp/Grpc.Core/Internal/INativeCall.cs85
-rw-r--r--src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs2
-rw-r--r--src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs2
-rw-r--r--src/csharp/Grpc.Core/Marshaller.cs2
-rw-r--r--src/csharp/Grpc.Core/Metadata.cs32
-rw-r--r--src/csharp/Grpc.Core/Method.cs2
-rw-r--r--src/csharp/Grpc.Core/Server.cs8
-rw-r--r--src/csharp/Grpc.Core/ServerCallContext.cs10
-rw-r--r--src/csharp/Grpc.Core/ServerMethods.cs8
-rw-r--r--src/csharp/Grpc.Core/ServerServiceDefinition.cs50
-rw-r--r--src/csharp/Grpc.Core/Utils/Preconditions.cs9
-rw-r--r--src/csharp/Grpc.Core/VersionInfo.cs4
-rw-r--r--src/csharp/Grpc.Core/WriteOptions.cs7
38 files changed, 522 insertions, 194 deletions
diff --git a/src/csharp/Grpc.Core/AsyncClientStreamingCall.cs b/src/csharp/Grpc.Core/AsyncClientStreamingCall.cs
index fb9b562c77..5646fed3d9 100644
--- a/src/csharp/Grpc.Core/AsyncClientStreamingCall.cs
+++ b/src/csharp/Grpc.Core/AsyncClientStreamingCall.cs
@@ -40,18 +40,22 @@ namespace Grpc.Core
/// <summary>
/// Return type for client streaming calls.
/// </summary>
+ /// <typeparam name="TRequest">Request message type for this call.</typeparam>
+ /// <typeparam name="TResponse">Response message type for this call.</typeparam>
public sealed class AsyncClientStreamingCall<TRequest, TResponse> : IDisposable
{
readonly IClientStreamWriter<TRequest> requestStream;
readonly Task<TResponse> responseAsync;
+ readonly Task<Metadata> responseHeadersAsync;
readonly Func<Status> getStatusFunc;
readonly Func<Metadata> getTrailersFunc;
readonly Action disposeAction;
- public AsyncClientStreamingCall(IClientStreamWriter<TRequest> requestStream, Task<TResponse> responseAsync, Func<Status> getStatusFunc, Func<Metadata> getTrailersFunc, Action disposeAction)
+ internal AsyncClientStreamingCall(IClientStreamWriter<TRequest> requestStream, Task<TResponse> responseAsync, Task<Metadata> responseHeadersAsync, Func<Status> getStatusFunc, Func<Metadata> getTrailersFunc, Action disposeAction)
{
this.requestStream = requestStream;
this.responseAsync = responseAsync;
+ this.responseHeadersAsync = responseHeadersAsync;
this.getStatusFunc = getStatusFunc;
this.getTrailersFunc = getTrailersFunc;
this.disposeAction = disposeAction;
@@ -69,6 +73,17 @@ namespace Grpc.Core
}
/// <summary>
+ /// Asynchronous access to response headers.
+ /// </summary>
+ public Task<Metadata> ResponseHeadersAsync
+ {
+ get
+ {
+ return this.responseHeadersAsync;
+ }
+ }
+
+ /// <summary>
/// Async stream to send streaming requests.
/// </summary>
public IClientStreamWriter<TRequest> RequestStream
diff --git a/src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs b/src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs
index 183c84216a..e75108c7e5 100644
--- a/src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs
+++ b/src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs
@@ -32,24 +32,29 @@
#endregion
using System;
+using System.Threading.Tasks;
namespace Grpc.Core
{
/// <summary>
/// Return type for bidirectional streaming calls.
/// </summary>
+ /// <typeparam name="TRequest">Request message type for this call.</typeparam>
+ /// <typeparam name="TResponse">Response message type for this call.</typeparam>
public sealed class AsyncDuplexStreamingCall<TRequest, TResponse> : IDisposable
{
readonly IClientStreamWriter<TRequest> requestStream;
readonly IAsyncStreamReader<TResponse> responseStream;
+ readonly Task<Metadata> responseHeadersAsync;
readonly Func<Status> getStatusFunc;
readonly Func<Metadata> getTrailersFunc;
readonly Action disposeAction;
- public AsyncDuplexStreamingCall(IClientStreamWriter<TRequest> requestStream, IAsyncStreamReader<TResponse> responseStream, Func<Status> getStatusFunc, Func<Metadata> getTrailersFunc, Action disposeAction)
+ internal AsyncDuplexStreamingCall(IClientStreamWriter<TRequest> requestStream, IAsyncStreamReader<TResponse> responseStream, Task<Metadata> responseHeadersAsync, Func<Status> getStatusFunc, Func<Metadata> getTrailersFunc, Action disposeAction)
{
this.requestStream = requestStream;
this.responseStream = responseStream;
+ this.responseHeadersAsync = responseHeadersAsync;
this.getStatusFunc = getStatusFunc;
this.getTrailersFunc = getTrailersFunc;
this.disposeAction = disposeAction;
@@ -78,6 +83,17 @@ namespace Grpc.Core
}
/// <summary>
+ /// Asynchronous access to response headers.
+ /// </summary>
+ public Task<Metadata> ResponseHeadersAsync
+ {
+ get
+ {
+ return this.responseHeadersAsync;
+ }
+ }
+
+ /// <summary>
/// Gets the call status if the call has already finished.
/// Throws InvalidOperationException otherwise.
/// </summary>
diff --git a/src/csharp/Grpc.Core/AsyncServerStreamingCall.cs b/src/csharp/Grpc.Core/AsyncServerStreamingCall.cs
index ab2049f269..f953091984 100644
--- a/src/csharp/Grpc.Core/AsyncServerStreamingCall.cs
+++ b/src/csharp/Grpc.Core/AsyncServerStreamingCall.cs
@@ -32,22 +32,26 @@
#endregion
using System;
+using System.Threading.Tasks;
namespace Grpc.Core
{
/// <summary>
/// Return type for server streaming calls.
/// </summary>
+ /// <typeparam name="TResponse">Response message type for this call.</typeparam>
public sealed class AsyncServerStreamingCall<TResponse> : IDisposable
{
readonly IAsyncStreamReader<TResponse> responseStream;
+ readonly Task<Metadata> responseHeadersAsync;
readonly Func<Status> getStatusFunc;
readonly Func<Metadata> getTrailersFunc;
readonly Action disposeAction;
- public AsyncServerStreamingCall(IAsyncStreamReader<TResponse> responseStream, Func<Status> getStatusFunc, Func<Metadata> getTrailersFunc, Action disposeAction)
+ internal AsyncServerStreamingCall(IAsyncStreamReader<TResponse> responseStream, Task<Metadata> responseHeadersAsync, Func<Status> getStatusFunc, Func<Metadata> getTrailersFunc, Action disposeAction)
{
this.responseStream = responseStream;
+ this.responseHeadersAsync = responseHeadersAsync;
this.getStatusFunc = getStatusFunc;
this.getTrailersFunc = getTrailersFunc;
this.disposeAction = disposeAction;
@@ -65,6 +69,17 @@ namespace Grpc.Core
}
/// <summary>
+ /// Asynchronous access to response headers.
+ /// </summary>
+ public Task<Metadata> ResponseHeadersAsync
+ {
+ get
+ {
+ return this.responseHeadersAsync;
+ }
+ }
+
+ /// <summary>
/// Gets the call status if the call has already finished.
/// Throws InvalidOperationException otherwise.
/// </summary>
diff --git a/src/csharp/Grpc.Core/AsyncUnaryCall.cs b/src/csharp/Grpc.Core/AsyncUnaryCall.cs
index 224e343916..97df8f5e91 100644
--- a/src/csharp/Grpc.Core/AsyncUnaryCall.cs
+++ b/src/csharp/Grpc.Core/AsyncUnaryCall.cs
@@ -40,16 +40,19 @@ namespace Grpc.Core
/// <summary>
/// Return type for single request - single response call.
/// </summary>
+ /// <typeparam name="TResponse">Response message type for this call.</typeparam>
public sealed class AsyncUnaryCall<TResponse> : IDisposable
{
readonly Task<TResponse> responseAsync;
+ readonly Task<Metadata> responseHeadersAsync;
readonly Func<Status> getStatusFunc;
readonly Func<Metadata> getTrailersFunc;
readonly Action disposeAction;
- public AsyncUnaryCall(Task<TResponse> responseAsync, Func<Status> getStatusFunc, Func<Metadata> getTrailersFunc, Action disposeAction)
+ internal AsyncUnaryCall(Task<TResponse> responseAsync, Task<Metadata> responseHeadersAsync, Func<Status> getStatusFunc, Func<Metadata> getTrailersFunc, Action disposeAction)
{
this.responseAsync = responseAsync;
+ this.responseHeadersAsync = responseHeadersAsync;
this.getStatusFunc = getStatusFunc;
this.getTrailersFunc = getTrailersFunc;
this.disposeAction = disposeAction;
@@ -67,6 +70,17 @@ namespace Grpc.Core
}
/// <summary>
+ /// Asynchronous access to response headers.
+ /// </summary>
+ public Task<Metadata> ResponseHeadersAsync
+ {
+ get
+ {
+ return this.responseHeadersAsync;
+ }
+ }
+
+ /// <summary>
/// Allows awaiting this object directly.
/// </summary>
public TaskAwaiter<TResponse> GetAwaiter()
diff --git a/src/csharp/Grpc.Core/CallInvocationDetails.cs b/src/csharp/Grpc.Core/CallInvocationDetails.cs
index 6565073fc5..8228b8f317 100644
--- a/src/csharp/Grpc.Core/CallInvocationDetails.cs
+++ b/src/csharp/Grpc.Core/CallInvocationDetails.cs
@@ -40,6 +40,8 @@ namespace Grpc.Core
/// <summary>
/// Details about a client-side call to be invoked.
/// </summary>
+ /// <typeparam name="TRequest">Request message type for the call.</typeparam>
+ /// <typeparam name="TResponse">Response message type for the call.</typeparam>
public struct CallInvocationDetails<TRequest, TResponse>
{
readonly Channel channel;
@@ -50,7 +52,7 @@ namespace Grpc.Core
CallOptions options;
/// <summary>
- /// Initializes a new instance of the <see cref="Grpc.Core.CallInvocationDetails`2"/> struct.
+ /// Initializes a new instance of the <see cref="Grpc.Core.CallInvocationDetails{TRequest,TResponse}"/> struct.
/// </summary>
/// <param name="channel">Channel to use for this call.</param>
/// <param name="method">Method to call.</param>
@@ -61,7 +63,7 @@ namespace Grpc.Core
}
/// <summary>
- /// Initializes a new instance of the <see cref="Grpc.Core.CallInvocationDetails`2"/> struct.
+ /// Initializes a new instance of the <see cref="Grpc.Core.CallInvocationDetails{TRequest,TResponse}"/> struct.
/// </summary>
/// <param name="channel">Channel to use for this call.</param>
/// <param name="method">Method to call.</param>
@@ -73,7 +75,7 @@ namespace Grpc.Core
}
/// <summary>
- /// Initializes a new instance of the <see cref="Grpc.Core.CallInvocationDetails`2"/> struct.
+ /// Initializes a new instance of the <see cref="Grpc.Core.CallInvocationDetails{TRequest,TResponse}"/> struct.
/// </summary>
/// <param name="channel">Channel to use for this call.</param>
/// <param name="method">Qualified method name.</param>
@@ -158,7 +160,7 @@ namespace Grpc.Core
}
/// <summary>
- /// Returns new instance of <see cref="CallInvocationDetails"/> with
+ /// Returns new instance of <see cref="CallInvocationDetails{TRequest, TResponse}"/> with
/// <c>Options</c> set to the value provided. Values of all other fields are preserved.
/// </summary>
public CallInvocationDetails<TRequest, TResponse> WithOptions(CallOptions options)
diff --git a/src/csharp/Grpc.Core/CallOptions.cs b/src/csharp/Grpc.Core/CallOptions.cs
index 3dfe80b48c..c3bc9c3156 100644
--- a/src/csharp/Grpc.Core/CallOptions.cs
+++ b/src/csharp/Grpc.Core/CallOptions.cs
@@ -118,6 +118,7 @@ namespace Grpc.Core
/// Returns new instance of <see cref="CallOptions"/> with
/// <c>Headers</c> set to the value provided. Values of all other fields are preserved.
/// </summary>
+ /// <param name="headers">The headers.</param>
public CallOptions WithHeaders(Metadata headers)
{
var newOptions = this;
@@ -129,6 +130,7 @@ namespace Grpc.Core
/// Returns new instance of <see cref="CallOptions"/> with
/// <c>Deadline</c> set to the value provided. Values of all other fields are preserved.
/// </summary>
+ /// <param name="deadline">The deadline.</param>
public CallOptions WithDeadline(DateTime deadline)
{
var newOptions = this;
@@ -140,6 +142,7 @@ namespace Grpc.Core
/// Returns new instance of <see cref="CallOptions"/> with
/// <c>CancellationToken</c> set to the value provided. Values of all other fields are preserved.
/// </summary>
+ /// <param name="cancellationToken">The cancellation token.</param>
public CallOptions WithCancellationToken(CancellationToken cancellationToken)
{
var newOptions = this;
diff --git a/src/csharp/Grpc.Core/Calls.cs b/src/csharp/Grpc.Core/Calls.cs
index 7067456638..94b3c2fe65 100644
--- a/src/csharp/Grpc.Core/Calls.cs
+++ b/src/csharp/Grpc.Core/Calls.cs
@@ -74,7 +74,7 @@ namespace Grpc.Core
{
var asyncCall = new AsyncCall<TRequest, TResponse>(call);
var asyncResult = asyncCall.UnaryCallAsync(req);
- return new AsyncUnaryCall<TResponse>(asyncResult, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel);
+ return new AsyncUnaryCall<TResponse>(asyncResult, asyncCall.ResponseHeadersAsync, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel);
}
/// <summary>
@@ -93,13 +93,14 @@ namespace Grpc.Core
var asyncCall = new AsyncCall<TRequest, TResponse>(call);
asyncCall.StartServerStreamingCall(req);
var responseStream = new ClientResponseStream<TRequest, TResponse>(asyncCall);
- return new AsyncServerStreamingCall<TResponse>(responseStream, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel);
+ return new AsyncServerStreamingCall<TResponse>(responseStream, asyncCall.ResponseHeadersAsync, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel);
}
/// <summary>
/// Invokes a client streaming call asynchronously.
/// In client streaming scenario, client sends a stream of requests and server responds with a single response.
/// </summary>
+ /// <param name="call">The call defintion.</param>
/// <returns>An awaitable call object providing access to the response.</returns>
/// <typeparam name="TRequest">Type of request messages.</typeparam>
/// <typeparam name="TResponse">The of response message.</typeparam>
@@ -110,7 +111,7 @@ namespace Grpc.Core
var asyncCall = new AsyncCall<TRequest, TResponse>(call);
var resultTask = asyncCall.ClientStreamingCallAsync();
var requestStream = new ClientRequestStream<TRequest, TResponse>(asyncCall);
- return new AsyncClientStreamingCall<TRequest, TResponse>(requestStream, resultTask, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel);
+ return new AsyncClientStreamingCall<TRequest, TResponse>(requestStream, resultTask, asyncCall.ResponseHeadersAsync, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel);
}
/// <summary>
@@ -130,7 +131,7 @@ namespace Grpc.Core
asyncCall.StartDuplexStreamingCall();
var requestStream = new ClientRequestStream<TRequest, TResponse>(asyncCall);
var responseStream = new ClientResponseStream<TRequest, TResponse>(asyncCall);
- return new AsyncDuplexStreamingCall<TRequest, TResponse>(requestStream, responseStream, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel);
+ return new AsyncDuplexStreamingCall<TRequest, TResponse>(requestStream, responseStream, asyncCall.ResponseHeadersAsync, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel);
}
}
}
diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs
index 2f8519dfa3..f1942727cd 100644
--- a/src/csharp/Grpc.Core/Channel.cs
+++ b/src/csharp/Grpc.Core/Channel.cs
@@ -43,7 +43,9 @@ using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
- /// gRPC Channel
+ /// Represents a gRPC channel. Channels are an abstraction of long-lived connections to remote servers.
+ /// More client objects can reuse the same channel. Creating a channel is an expensive operation compared to invoking
+ /// a remote call so in general you should reuse a single channel for as many calls as possible.
/// </summary>
public class Channel
{
@@ -58,7 +60,6 @@ namespace Grpc.Core
readonly List<ChannelOption> options;
bool shutdownRequested;
- bool disposed;
/// <summary>
/// Creates a channel that connects to a specific host.
@@ -162,6 +163,7 @@ namespace Grpc.Core
/// There is no need to call this explicitly unless your use case requires that.
/// Starting an RPC on a new channel will request connection implicitly.
/// </summary>
+ /// <param name="deadline">The deadline. <c>null</c> indicates no deadline.</param>
public async Task ConnectAsync(DateTime? deadline = null)
{
var currentState = handle.CheckConnectivityState(true);
diff --git a/src/csharp/Grpc.Core/ChannelOptions.cs b/src/csharp/Grpc.Core/ChannelOptions.cs
index ad54b46ad5..f5ef63af54 100644
--- a/src/csharp/Grpc.Core/ChannelOptions.cs
+++ b/src/csharp/Grpc.Core/ChannelOptions.cs
@@ -44,9 +44,19 @@ namespace Grpc.Core
/// </summary>
public sealed class ChannelOption
{
+ /// <summary>
+ /// Type of <c>ChannelOption</c>.
+ /// </summary>
public enum OptionType
{
+ /// <summary>
+ /// Channel option with integer value.
+ /// </summary>
Integer,
+
+ /// <summary>
+ /// Channel option with string value.
+ /// </summary>
String
}
@@ -79,6 +89,9 @@ namespace Grpc.Core
this.intValue = intValue;
}
+ /// <summary>
+ /// Gets the type of the <c>ChannelOption</c>.
+ /// </summary>
public OptionType Type
{
get
@@ -87,6 +100,9 @@ namespace Grpc.Core
}
}
+ /// <summary>
+ /// Gets the name of the <c>ChannelOption</c>.
+ /// </summary>
public string Name
{
get
@@ -95,6 +111,9 @@ namespace Grpc.Core
}
}
+ /// <summary>
+ /// Gets the integer value the <c>ChannelOption</c>.
+ /// </summary>
public int IntValue
{
get
@@ -104,6 +123,9 @@ namespace Grpc.Core
}
}
+ /// <summary>
+ /// Gets the string value the <c>ChannelOption</c>.
+ /// </summary>
public string StringValue
{
get
@@ -140,7 +162,7 @@ namespace Grpc.Core
/// <summary>Primary user agent: goes at the start of the user-agent metadata</summary>
public const string PrimaryUserAgentString = "grpc.primary_user_agent";
- /// <summary> Secondary user agent: goes at the end of the user-agent metadata</summary>
+ /// <summary>Secondary user agent: goes at the end of the user-agent metadata</summary>
public const string SecondaryUserAgentString = "grpc.secondary_user_agent";
/// <summary>
diff --git a/src/csharp/Grpc.Core/ClientBase.cs b/src/csharp/Grpc.Core/ClientBase.cs
index 903449439b..f4533e735c 100644
--- a/src/csharp/Grpc.Core/ClientBase.cs
+++ b/src/csharp/Grpc.Core/ClientBase.cs
@@ -53,6 +53,10 @@ namespace Grpc.Core
readonly Channel channel;
readonly string authUriBase;
+ /// <summary>
+ /// Initializes a new instance of <c>ClientBase</c> class.
+ /// </summary>
+ /// <param name="channel">The channel to use for remote call invocation.</param>
public ClientBase(Channel channel)
{
this.channel = channel;
@@ -95,6 +99,11 @@ namespace Grpc.Core
/// <summary>
/// Creates a new call to given method.
/// </summary>
+ /// <param name="method">The method to invoke.</param>
+ /// <param name="options">The call options.</param>
+ /// <typeparam name="TRequest">Request message type.</typeparam>
+ /// <typeparam name="TResponse">Response message type.</typeparam>
+ /// <returns>The call invocation details.</returns>
protected CallInvocationDetails<TRequest, TResponse> CreateCall<TRequest, TResponse>(Method<TRequest, TResponse> method, CallOptions options)
where TRequest : class
where TResponse : class
diff --git a/src/csharp/Grpc.Core/ContextPropagationToken.cs b/src/csharp/Grpc.Core/ContextPropagationToken.cs
index a5bf1b5a70..1d899b97fd 100644
--- a/src/csharp/Grpc.Core/ContextPropagationToken.cs
+++ b/src/csharp/Grpc.Core/ContextPropagationToken.cs
@@ -44,8 +44,8 @@ namespace Grpc.Core
/// In situations when a backend is making calls to another backend,
/// it makes sense to propagate properties like deadline and cancellation
/// token of the server call to the child call.
- /// C core provides some other contexts (like tracing context) that
- /// are not accessible to C# layer, but this token still allows propagating them.
+ /// The gRPC native layer provides some other contexts (like tracing context) that
+ /// are not accessible to explicitly C# layer, but this token still allows propagating them.
/// </summary>
public class ContextPropagationToken
{
@@ -143,13 +143,13 @@ namespace Grpc.Core
this.propagateCancellation = propagateCancellation;
}
- /// <value><c>true</c> if parent call's deadline should be propagated to the child call.</value>
+ /// <summary><c>true</c> if parent call's deadline should be propagated to the child call.</summary>
public bool IsPropagateDeadline
{
get { return this.propagateDeadline; }
}
- /// <value><c>true</c> if parent call's cancellation token should be propagated to the child call.</value>
+ /// <summary><c>true</c> if parent call's cancellation token should be propagated to the child call.</summary>
public bool IsPropagateCancellation
{
get { return this.propagateCancellation; }
diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj
index 055aff1444..ad2af17bc7 100644
--- a/src/csharp/Grpc.Core/Grpc.Core.csproj
+++ b/src/csharp/Grpc.Core/Grpc.Core.csproj
@@ -49,6 +49,7 @@
<Compile Include="AsyncDuplexStreamingCall.cs" />
<Compile Include="AsyncServerStreamingCall.cs" />
<Compile Include="IClientStreamWriter.cs" />
+ <Compile Include="Internal\INativeCall.cs" />
<Compile Include="IServerStreamWriter.cs" />
<Compile Include="IAsyncStreamWriter.cs" />
<Compile Include="IAsyncStreamReader.cs" />
diff --git a/src/csharp/Grpc.Core/GrpcEnvironment.cs b/src/csharp/Grpc.Core/GrpcEnvironment.cs
index 0a44eead74..e7c04185c2 100644
--- a/src/csharp/Grpc.Core/GrpcEnvironment.cs
+++ b/src/csharp/Grpc.Core/GrpcEnvironment.cs
@@ -102,6 +102,14 @@ namespace Grpc.Core
}
}
+ internal static int GetRefCount()
+ {
+ lock (staticLock)
+ {
+ return refCount;
+ }
+ }
+
/// <summary>
/// Gets application-wide logger used by gRPC.
/// </summary>
@@ -177,7 +185,6 @@ namespace Grpc.Core
return Marshal.PtrToStringAnsi(ptr);
}
-
internal static void GrpcNativeInit()
{
grpcsharp_init();
diff --git a/src/csharp/Grpc.Core/IAsyncStreamReader.cs b/src/csharp/Grpc.Core/IAsyncStreamReader.cs
index c0a0674e50..49e1ea7832 100644
--- a/src/csharp/Grpc.Core/IAsyncStreamReader.cs
+++ b/src/csharp/Grpc.Core/IAsyncStreamReader.cs
@@ -42,7 +42,7 @@ namespace Grpc.Core
/// <summary>
/// A stream of messages to be read.
/// </summary>
- /// <typeparam name="T"></typeparam>
+ /// <typeparam name="T">The message type.</typeparam>
public interface IAsyncStreamReader<T> : IAsyncEnumerator<T>
{
// TODO(jtattermusch): consider just using IAsyncEnumerator instead of this interface.
diff --git a/src/csharp/Grpc.Core/IAsyncStreamWriter.cs b/src/csharp/Grpc.Core/IAsyncStreamWriter.cs
index 4e2acb9c71..9c0d2d312e 100644
--- a/src/csharp/Grpc.Core/IAsyncStreamWriter.cs
+++ b/src/csharp/Grpc.Core/IAsyncStreamWriter.cs
@@ -42,7 +42,7 @@ namespace Grpc.Core
/// <summary>
/// A writable stream of messages.
/// </summary>
- /// <typeparam name="T"></typeparam>
+ /// <typeparam name="T">The message type.</typeparam>
public interface IAsyncStreamWriter<T>
{
/// <summary>
@@ -56,7 +56,7 @@ namespace Grpc.Core
/// If null, default options will be used.
/// Once set, this property maintains its value across subsequent
/// writes.
- /// <value>The write options.</value>
+ /// </summary>
WriteOptions WriteOptions { get; set; }
}
}
diff --git a/src/csharp/Grpc.Core/IClientStreamWriter.cs b/src/csharp/Grpc.Core/IClientStreamWriter.cs
index a3028bc374..3fd0774db5 100644
--- a/src/csharp/Grpc.Core/IClientStreamWriter.cs
+++ b/src/csharp/Grpc.Core/IClientStreamWriter.cs
@@ -42,7 +42,7 @@ namespace Grpc.Core
/// <summary>
/// Client-side writable stream of messages with Close capability.
/// </summary>
- /// <typeparam name="T"></typeparam>
+ /// <typeparam name="T">The message type.</typeparam>
public interface IClientStreamWriter<T> : IAsyncStreamWriter<T>
{
/// <summary>
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
index bb9ba5b8dd..e3b00781c6 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
@@ -51,22 +51,35 @@ namespace Grpc.Core.Internal
static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<AsyncCall<TRequest, TResponse>>();
readonly CallInvocationDetails<TRequest, TResponse> details;
+ readonly INativeCall injectedNativeCall; // for testing
// Completion of a pending unary response if not null.
TaskCompletionSource<TResponse> unaryResponseTcs;
+ // Indicates that steaming call has finished.
+ TaskCompletionSource<object> streamingCallFinishedTcs = new TaskCompletionSource<object>();
+
+ // Response headers set here once received.
+ TaskCompletionSource<Metadata> responseHeadersTcs = new TaskCompletionSource<Metadata>();
+
// Set after status is received. Used for both unary and streaming response calls.
ClientSideStatus? finishedStatus;
- bool readObserverCompleted; // True if readObserver has already been completed.
-
public AsyncCall(CallInvocationDetails<TRequest, TResponse> callDetails)
- : base(callDetails.RequestMarshaller.Serializer, callDetails.ResponseMarshaller.Deserializer)
+ : base(callDetails.RequestMarshaller.Serializer, callDetails.ResponseMarshaller.Deserializer, callDetails.Channel.Environment)
{
this.details = callDetails.WithOptions(callDetails.Options.Normalize());
this.initialMetadataSent = true; // we always send metadata at the very beginning of the call.
}
+ /// <summary>
+ /// This constructor should only be used for testing.
+ /// </summary>
+ public AsyncCall(CallInvocationDetails<TRequest, TResponse> callDetails, INativeCall injectedNativeCall) : this(callDetails)
+ {
+ this.injectedNativeCall = injectedNativeCall;
+ }
+
// TODO: this method is not Async, so it shouldn't be in AsyncCall class, but
// it is reusing fair amount of code in this class, so we are leaving it here.
/// <summary>
@@ -100,7 +113,7 @@ namespace Grpc.Core.Internal
bool success = (ev.success != 0);
try
{
- HandleUnaryResponse(success, ctx);
+ HandleUnaryResponse(success, ctx.GetReceivedStatusOnClient(), ctx.GetReceivedMessage(), ctx.GetReceivedInitialMetadata());
}
catch (Exception e)
{
@@ -125,7 +138,7 @@ namespace Grpc.Core.Internal
Preconditions.CheckState(!started);
started = true;
- Initialize(details.Channel.Environment.CompletionQueue);
+ Initialize(environment.CompletionQueue);
halfcloseRequested = true;
readingDone = true;
@@ -152,7 +165,7 @@ namespace Grpc.Core.Internal
Preconditions.CheckState(!started);
started = true;
- Initialize(details.Channel.Environment.CompletionQueue);
+ Initialize(environment.CompletionQueue);
readingDone = true;
@@ -176,10 +189,9 @@ namespace Grpc.Core.Internal
Preconditions.CheckState(!started);
started = true;
- Initialize(details.Channel.Environment.CompletionQueue);
+ Initialize(environment.CompletionQueue);
halfcloseRequested = true;
- halfclosed = true; // halfclose not confirmed yet, but it will be once finishedHandler is called.
byte[] payload = UnsafeSerialize(msg);
@@ -187,6 +199,7 @@ namespace Grpc.Core.Internal
{
call.StartServerStreaming(HandleFinished, payload, metadataArray, GetWriteFlagsForCall());
}
+ call.StartReceiveInitialMetadata(HandleReceivedResponseHeaders);
}
}
@@ -201,12 +214,13 @@ namespace Grpc.Core.Internal
Preconditions.CheckState(!started);
started = true;
- Initialize(details.Channel.Environment.CompletionQueue);
+ Initialize(environment.CompletionQueue);
using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
{
call.StartDuplexStreaming(HandleFinished, metadataArray);
}
+ call.StartReceiveInitialMetadata(HandleReceivedResponseHeaders);
}
}
@@ -248,6 +262,28 @@ namespace Grpc.Core.Internal
}
/// <summary>
+ /// Get the task that completes once if streaming call finishes with ok status and throws RpcException with given status otherwise.
+ /// </summary>
+ public Task StreamingCallFinishedTask
+ {
+ get
+ {
+ return streamingCallFinishedTcs.Task;
+ }
+ }
+
+ /// <summary>
+ /// Get the task that completes once response headers are received.
+ /// </summary>
+ public Task<Metadata> ResponseHeadersAsync
+ {
+ get
+ {
+ return responseHeadersTcs.Task;
+ }
+ }
+
+ /// <summary>
/// Gets the resulting status if the call has already finished.
/// Throws InvalidOperationException otherwise.
/// </summary>
@@ -281,53 +317,36 @@ namespace Grpc.Core.Internal
}
}
- /// <summary>
- /// On client-side, we only fire readCompletionDelegate once all messages have been read
- /// and status has been received.
- /// </summary>
- protected override void ProcessLastRead(AsyncCompletionDelegate<TResponse> completionDelegate)
+ protected override void OnAfterReleaseResources()
{
- if (completionDelegate != null && readingDone && finishedStatus.HasValue)
- {
- bool shouldComplete;
- lock (myLock)
- {
- shouldComplete = !readObserverCompleted;
- readObserverCompleted = true;
- }
-
- if (shouldComplete)
- {
- var status = finishedStatus.Value.Status;
- if (status.StatusCode != StatusCode.OK)
- {
- FireCompletion(completionDelegate, default(TResponse), new RpcException(status));
- }
- else
- {
- FireCompletion(completionDelegate, default(TResponse), null);
- }
- }
- }
+ details.Channel.RemoveCallReference(this);
}
- protected override void OnAfterReleaseResources()
+ protected override bool IsClient
{
- details.Channel.RemoveCallReference(this);
+ get { return true; }
}
private void Initialize(CompletionQueueSafeHandle cq)
{
+ var call = CreateNativeCall(cq);
+ details.Channel.AddCallReference(this);
+ InitializeInternal(call);
+ RegisterCancellationCallback();
+ }
+
+ private INativeCall CreateNativeCall(CompletionQueueSafeHandle cq)
+ {
+ if (injectedNativeCall != null)
+ {
+ return injectedNativeCall; // allows injecting a mock INativeCall in tests.
+ }
+
var parentCall = details.Options.PropagationToken != null ? details.Options.PropagationToken.ParentCall : CallSafeHandle.NullInstance;
- var call = details.Channel.Handle.CreateCall(details.Channel.Environment.CompletionRegistry,
+ return details.Channel.Handle.CreateCall(environment.CompletionRegistry,
parentCall, ContextPropagationToken.DefaultMask, cq,
details.Method, details.Host, Timespec.FromDateTime(details.Options.Deadline.Value));
-
- details.Channel.AddCallReference(this);
-
- InitializeInternal(call);
- RegisterCancellationCallback();
}
// Make sure that once cancellationToken for this call is cancelled, Cancel() will be called.
@@ -350,62 +369,69 @@ namespace Grpc.Core.Internal
}
/// <summary>
+ /// Handles receive status completion for calls with streaming response.
+ /// </summary>
+ private void HandleReceivedResponseHeaders(bool success, Metadata responseHeaders)
+ {
+ responseHeadersTcs.SetResult(responseHeaders);
+ }
+
+ /// <summary>
/// Handler for unary response completion.
/// </summary>
- private void HandleUnaryResponse(bool success, BatchContextSafeHandle ctx)
+ private void HandleUnaryResponse(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders)
{
- var fullStatus = ctx.GetReceivedStatusOnClient();
+ TResponse msg = default(TResponse);
+ var deserializeException = success ? TryDeserialize(receivedMessage, out msg) : null;
lock (myLock)
{
finished = true;
- finishedStatus = fullStatus;
- halfclosed = true;
+ if (deserializeException != null && receivedStatus.Status.StatusCode == StatusCode.OK)
+ {
+ receivedStatus = new ClientSideStatus(DeserializeResponseFailureStatus, receivedStatus.Trailers);
+ }
+ finishedStatus = receivedStatus;
ReleaseResourcesIfPossible();
}
- if (!success)
- {
- unaryResponseTcs.SetException(new RpcException(new Status(StatusCode.Internal, "Internal error occured.")));
- return;
- }
+ responseHeadersTcs.SetResult(responseHeaders);
- var status = fullStatus.Status;
+ var status = receivedStatus.Status;
- if (status.StatusCode != StatusCode.OK)
+ if (!success || status.StatusCode != StatusCode.OK)
{
unaryResponseTcs.SetException(new RpcException(status));
return;
}
- // TODO: handle deserialization error
- TResponse msg;
- TryDeserialize(ctx.GetReceivedMessage(), out msg);
-
unaryResponseTcs.SetResult(msg);
}
/// <summary>
/// Handles receive status completion for calls with streaming response.
/// </summary>
- private void HandleFinished(bool success, BatchContextSafeHandle ctx)
+ private void HandleFinished(bool success, ClientSideStatus receivedStatus)
{
- var fullStatus = ctx.GetReceivedStatusOnClient();
-
- AsyncCompletionDelegate<TResponse> origReadCompletionDelegate = null;
lock (myLock)
{
finished = true;
- finishedStatus = fullStatus;
-
- origReadCompletionDelegate = readCompletionDelegate;
+ finishedStatus = receivedStatus;
ReleaseResourcesIfPossible();
}
- ProcessLastRead(origReadCompletionDelegate);
+ var status = receivedStatus.Status;
+
+ if (!success || status.StatusCode != StatusCode.OK)
+ {
+ streamingCallFinishedTcs.SetException(new RpcException(status));
+ return;
+ }
+
+ streamingCallFinishedTcs.SetResult(null);
}
}
} \ No newline at end of file
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
index 1808294f43..3e2c57c9b5 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
@@ -33,10 +33,12 @@
using System;
using System.Diagnostics;
+using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
+
using Grpc.Core.Internal;
using Grpc.Core.Logging;
using Grpc.Core.Utils;
@@ -50,34 +52,35 @@ namespace Grpc.Core.Internal
internal abstract class AsyncCallBase<TWrite, TRead>
{
static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<AsyncCallBase<TWrite, TRead>>();
+ protected static readonly Status DeserializeResponseFailureStatus = new Status(StatusCode.Internal, "Failed to deserialize response message.");
readonly Func<TWrite, byte[]> serializer;
readonly Func<byte[], TRead> deserializer;
+ protected readonly GrpcEnvironment environment;
protected readonly object myLock = new object();
- protected CallSafeHandle call;
+ protected INativeCall call;
protected bool disposed;
protected bool started;
- protected bool errorOccured;
protected bool cancelRequested;
protected AsyncCompletionDelegate<object> sendCompletionDelegate; // Completion of a pending send or sendclose if not null.
protected AsyncCompletionDelegate<TRead> readCompletionDelegate; // Completion of a pending send or sendclose if not null.
- protected bool readingDone;
- protected bool halfcloseRequested;
- protected bool halfclosed;
+ protected bool readingDone; // True if last read (i.e. read with null payload) was already received.
+ protected bool halfcloseRequested; // True if send close have been initiated.
protected bool finished; // True if close has been received from the peer.
protected bool initialMetadataSent;
- protected long streamingWritesCounter;
+ protected long streamingWritesCounter; // Number of streaming send operations started so far.
- public AsyncCallBase(Func<TWrite, byte[]> serializer, Func<byte[], TRead> deserializer)
+ public AsyncCallBase(Func<TWrite, byte[]> serializer, Func<byte[], TRead> deserializer, GrpcEnvironment environment)
{
this.serializer = Preconditions.CheckNotNull(serializer);
this.deserializer = Preconditions.CheckNotNull(deserializer);
+ this.environment = Preconditions.CheckNotNull(environment);
}
/// <summary>
@@ -100,11 +103,10 @@ namespace Grpc.Core.Internal
/// <summary>
/// Requests cancelling the call with given status.
/// </summary>
- public void CancelWithStatus(Status status)
+ protected void CancelWithStatus(Status status)
{
lock (myLock)
{
- Preconditions.CheckState(started);
cancelRequested = true;
if (!disposed)
@@ -114,7 +116,7 @@ namespace Grpc.Core.Internal
}
}
- protected void InitializeInternal(CallSafeHandle call)
+ protected void InitializeInternal(INativeCall call)
{
lock (myLock)
{
@@ -159,16 +161,6 @@ namespace Grpc.Core.Internal
}
}
- // TODO(jtattermusch): find more fitting name for this method.
- /// <summary>
- /// Default behavior just completes the read observer, but more sofisticated behavior might be required
- /// by subclasses.
- /// </summary>
- protected virtual void ProcessLastRead(AsyncCompletionDelegate<TRead> completionDelegate)
- {
- FireCompletion(completionDelegate, default(TRead), null);
- }
-
/// <summary>
/// If there are no more pending actions and no new actions can be started, releases
/// the underlying native resources.
@@ -177,7 +169,7 @@ namespace Grpc.Core.Internal
{
if (!disposed && call != null)
{
- bool noMoreSendCompletions = halfclosed || (cancelRequested && sendCompletionDelegate == null);
+ bool noMoreSendCompletions = sendCompletionDelegate == null && (halfcloseRequested || cancelRequested || finished);
if (noMoreSendCompletions && readingDone && finished)
{
ReleaseResources();
@@ -187,6 +179,11 @@ namespace Grpc.Core.Internal
return false;
}
+ protected abstract bool IsClient
+ {
+ get;
+ }
+
private void ReleaseResources()
{
if (call != null)
@@ -204,11 +201,11 @@ namespace Grpc.Core.Internal
protected void CheckSendingAllowed()
{
Preconditions.CheckState(started);
- Preconditions.CheckState(!errorOccured);
CheckNotCancelled();
Preconditions.CheckState(!disposed);
Preconditions.CheckState(!halfcloseRequested, "Already halfclosed.");
+ Preconditions.CheckState(!finished, "Already finished.");
Preconditions.CheckState(sendCompletionDelegate == null, "Only one write can be pending at a time");
}
@@ -216,7 +213,6 @@ namespace Grpc.Core.Internal
{
Preconditions.CheckState(started);
Preconditions.CheckState(!disposed);
- Preconditions.CheckState(!errorOccured);
Preconditions.CheckState(!readingDone, "Stream has already been closed.");
Preconditions.CheckState(readCompletionDelegate == null, "Only one read can be pending at a time");
@@ -235,33 +231,31 @@ namespace Grpc.Core.Internal
return serializer(msg);
}
- protected bool TrySerialize(TWrite msg, out byte[] payload)
+ protected Exception TrySerialize(TWrite msg, out byte[] payload)
{
try
{
payload = serializer(msg);
- return true;
+ return null;
}
catch (Exception e)
{
- Logger.Error(e, "Exception occured while trying to serialize message");
payload = null;
- return false;
+ return e;
}
}
- protected bool TryDeserialize(byte[] payload, out TRead msg)
+ protected Exception TryDeserialize(byte[] payload, out TRead msg)
{
try
{
msg = deserializer(payload);
- return true;
+ return null;
}
catch (Exception e)
{
- Logger.Error(e, "Exception occured while trying to deserialize message.");
msg = default(TRead);
- return false;
+ return e;
}
}
@@ -280,7 +274,7 @@ namespace Grpc.Core.Internal
/// <summary>
/// Handles send completion.
/// </summary>
- protected void HandleSendFinished(bool success, BatchContextSafeHandle ctx)
+ protected void HandleSendFinished(bool success)
{
AsyncCompletionDelegate<object> origCompletionDelegate = null;
lock (myLock)
@@ -304,12 +298,11 @@ namespace Grpc.Core.Internal
/// <summary>
/// Handles halfclose completion.
/// </summary>
- protected void HandleHalfclosed(bool success, BatchContextSafeHandle ctx)
+ protected void HandleHalfclosed(bool success)
{
AsyncCompletionDelegate<object> origCompletionDelegate = null;
lock (myLock)
{
- halfclosed = true;
origCompletionDelegate = sendCompletionDelegate;
sendCompletionDelegate = null;
@@ -329,43 +322,40 @@ namespace Grpc.Core.Internal
/// <summary>
/// Handles streaming read completion.
/// </summary>
- protected void HandleReadFinished(bool success, BatchContextSafeHandle ctx)
+ protected void HandleReadFinished(bool success, byte[] receivedMessage)
{
- var payload = ctx.GetReceivedMessage();
+ TRead msg = default(TRead);
+ var deserializeException = (success && receivedMessage != null) ? TryDeserialize(receivedMessage, out msg) : null;
AsyncCompletionDelegate<TRead> origCompletionDelegate = null;
lock (myLock)
{
origCompletionDelegate = readCompletionDelegate;
- if (payload != null)
+ readCompletionDelegate = null;
+
+ if (receivedMessage == null)
{
- readCompletionDelegate = null;
+ // This was the last read.
+ readingDone = true;
}
- else
+
+ if (deserializeException != null && IsClient)
{
- // This was the last read. Keeping the readCompletionDelegate
- // to be either fired by this handler or by client-side finished
- // handler.
readingDone = true;
+ CancelWithStatus(DeserializeResponseFailureStatus);
}
ReleaseResourcesIfPossible();
}
- // TODO: handle the case when error occured...
+ // TODO: handle the case when success==false
- if (payload != null)
- {
- // TODO: handle deserialization error
- TRead msg;
- TryDeserialize(payload, out msg);
-
- FireCompletion(origCompletionDelegate, msg, null);
- }
- else
+ if (deserializeException != null && !IsClient)
{
- ProcessLastRead(origCompletionDelegate);
+ FireCompletion(origCompletionDelegate, default(TRead), new IOException("Failed to deserialize request message.", deserializeException));
+ return;
}
+ FireCompletion(origCompletionDelegate, msg, null);
}
}
} \ No newline at end of file
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
index 6278c0191e..46ca459349 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
@@ -49,12 +49,10 @@ namespace Grpc.Core.Internal
{
readonly TaskCompletionSource<object> finishedServersideTcs = new TaskCompletionSource<object>();
readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
- readonly GrpcEnvironment environment;
readonly Server server;
- public AsyncCallServer(Func<TResponse, byte[]> serializer, Func<byte[], TRequest> deserializer, GrpcEnvironment environment, Server server) : base(serializer, deserializer)
+ public AsyncCallServer(Func<TResponse, byte[]> serializer, Func<byte[], TRequest> deserializer, GrpcEnvironment environment, Server server) : base(serializer, deserializer, environment)
{
- this.environment = Preconditions.CheckNotNull(environment);
this.server = Preconditions.CheckNotNull(server);
}
@@ -171,6 +169,11 @@ namespace Grpc.Core.Internal
}
}
+ protected override bool IsClient
+ {
+ get { return false; }
+ }
+
protected override void CheckReadingAllowed()
{
base.CheckReadingAllowed();
@@ -185,10 +188,8 @@ namespace Grpc.Core.Internal
/// <summary>
/// Handles the server side close completion.
/// </summary>
- private void HandleFinishedServerside(bool success, BatchContextSafeHandle ctx)
+ private void HandleFinishedServerside(bool success, bool cancelled)
{
- bool cancelled = ctx.GetReceivedCloseOnServerCancelled();
-
lock (myLock)
{
finished = true;
diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
index 3cb01e29bd..c3611a7761 100644
--- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
@@ -38,9 +38,9 @@ using Grpc.Core.Utils;
namespace Grpc.Core.Internal
{
/// <summary>
- /// grpc_call from <grpc/grpc.h>
+ /// grpc_call from <c>grpc/grpc.h</c>
/// </summary>
- internal class CallSafeHandle : SafeHandleZeroIsInvalid
+ internal class CallSafeHandle : SafeHandleZeroIsInvalid, INativeCall
{
public static readonly CallSafeHandle NullInstance = new CallSafeHandle();
@@ -87,6 +87,10 @@ namespace Grpc.Core.Internal
BatchContextSafeHandle ctx);
[DllImport("grpc_csharp_ext.dll")]
+ static extern GRPCCallError grpcsharp_call_recv_initial_metadata(CallSafeHandle call,
+ BatchContextSafeHandle ctx);
+
+ [DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_start_serverside(CallSafeHandle call,
BatchContextSafeHandle ctx);
@@ -109,10 +113,10 @@ namespace Grpc.Core.Internal
this.completionRegistry = completionRegistry;
}
- public void StartUnary(BatchCompletionDelegate callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
+ public void StartUnary(UnaryResponseClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
{
var ctx = BatchContextSafeHandle.Create();
- completionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient(), context.GetReceivedMessage(), context.GetReceivedInitialMetadata()));
grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags)
.CheckOk();
}
@@ -123,66 +127,73 @@ namespace Grpc.Core.Internal
.CheckOk();
}
- public void StartClientStreaming(BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray)
+ public void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray)
{
var ctx = BatchContextSafeHandle.Create();
- completionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient(), context.GetReceivedMessage(), context.GetReceivedInitialMetadata()));
grpcsharp_call_start_client_streaming(this, ctx, metadataArray).CheckOk();
}
- public void StartServerStreaming(BatchCompletionDelegate callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
+ public void StartServerStreaming(ReceivedStatusOnClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
{
var ctx = BatchContextSafeHandle.Create();
- completionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient()));
grpcsharp_call_start_server_streaming(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags).CheckOk();
}
- public void StartDuplexStreaming(BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray)
+ public void StartDuplexStreaming(ReceivedStatusOnClientHandler callback, MetadataArraySafeHandle metadataArray)
{
var ctx = BatchContextSafeHandle.Create();
- completionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient()));
grpcsharp_call_start_duplex_streaming(this, ctx, metadataArray).CheckOk();
}
- public void StartSendMessage(BatchCompletionDelegate callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata)
+ public void StartSendMessage(SendCompletionHandler callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata)
{
var ctx = BatchContextSafeHandle.Create();
- completionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success));
grpcsharp_call_send_message(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, sendEmptyInitialMetadata).CheckOk();
}
- public void StartSendCloseFromClient(BatchCompletionDelegate callback)
+ public void StartSendCloseFromClient(SendCompletionHandler callback)
{
var ctx = BatchContextSafeHandle.Create();
- completionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success));
grpcsharp_call_send_close_from_client(this, ctx).CheckOk();
}
- public void StartSendStatusFromServer(BatchCompletionDelegate callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata)
+ public void StartSendStatusFromServer(SendCompletionHandler callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata)
{
var ctx = BatchContextSafeHandle.Create();
- completionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success));
grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, status.Detail, metadataArray, sendEmptyInitialMetadata).CheckOk();
}
- public void StartReceiveMessage(BatchCompletionDelegate callback)
+ public void StartReceiveMessage(ReceivedMessageHandler callback)
{
var ctx = BatchContextSafeHandle.Create();
- completionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedMessage()));
grpcsharp_call_recv_message(this, ctx).CheckOk();
}
- public void StartServerSide(BatchCompletionDelegate callback)
+ public void StartReceiveInitialMetadata(ReceivedResponseHeadersHandler callback)
+ {
+ var ctx = BatchContextSafeHandle.Create();
+ completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedInitialMetadata()));
+ grpcsharp_call_recv_initial_metadata(this, ctx).CheckOk();
+ }
+
+ public void StartServerSide(ReceivedCloseOnServerHandler callback)
{
var ctx = BatchContextSafeHandle.Create();
- completionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedCloseOnServerCancelled()));
grpcsharp_call_start_serverside(this, ctx).CheckOk();
}
- public void StartSendInitialMetadata(BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray)
+ public void StartSendInitialMetadata(SendCompletionHandler callback, MetadataArraySafeHandle metadataArray)
{
var ctx = BatchContextSafeHandle.Create();
- completionRegistry.RegisterBatchCompletion(ctx, callback);
+ completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success));
grpcsharp_call_send_initial_metadata(this, ctx, metadataArray).CheckOk();
}
diff --git a/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs
index c12aec5a3a..ea5b52374e 100644
--- a/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs
@@ -35,7 +35,7 @@ using System.Threading.Tasks;
namespace Grpc.Core.Internal
{
/// <summary>
- /// grpc_channel_args from <grpc/grpc.h>
+ /// grpc_channel_args from <c>grpc/grpc.h</c>
/// </summary>
internal class ChannelArgsSafeHandle : SafeHandleZeroIsInvalid
{
diff --git a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
index 8cef566c14..7a1c6e3dac 100644
--- a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
@@ -36,7 +36,7 @@ using System.Threading.Tasks;
namespace Grpc.Core.Internal
{
/// <summary>
- /// grpc_channel from <grpc/grpc.h>
+ /// grpc_channel from <c>grpc/grpc.h</c>
/// </summary>
internal class ChannelSafeHandle : SafeHandleZeroIsInvalid
{
diff --git a/src/csharp/Grpc.Core/Internal/ClientResponseStream.cs b/src/csharp/Grpc.Core/Internal/ClientResponseStream.cs
index 6c44521038..b4a7335c7c 100644
--- a/src/csharp/Grpc.Core/Internal/ClientResponseStream.cs
+++ b/src/csharp/Grpc.Core/Internal/ClientResponseStream.cs
@@ -72,7 +72,13 @@ namespace Grpc.Core.Internal
call.StartReadMessage(taskSource.CompletionDelegate);
var result = await taskSource.Task;
this.current = result;
- return result != null;
+
+ if (result == null)
+ {
+ await call.StreamingCallFinishedTask;
+ return false;
+ }
+ return true;
}
public void Dispose()
diff --git a/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs
index f64f3d4175..f7a3471bb4 100644
--- a/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs
@@ -35,7 +35,7 @@ using System.Threading.Tasks;
namespace Grpc.Core.Internal
{
/// <summary>
- /// grpc_completion_queue from <grpc/grpc.h>
+ /// grpc_completion_queue from <c>grpc/grpc.h</c>
/// </summary>
internal class CompletionQueueSafeHandle : SafeHandleZeroIsInvalid
{
diff --git a/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs
index 8b4fa85e5d..feed335362 100644
--- a/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs
@@ -36,7 +36,7 @@ using System.Threading.Tasks;
namespace Grpc.Core.Internal
{
/// <summary>
- /// grpc_credentials from <grpc/grpc_security.h>
+ /// grpc_credentials from <c>grpc/grpc_security.h</c>
/// </summary>
internal class CredentialsSafeHandle : SafeHandleZeroIsInvalid
{
diff --git a/src/csharp/Grpc.Core/Internal/INativeCall.cs b/src/csharp/Grpc.Core/Internal/INativeCall.cs
new file mode 100644
index 0000000000..cbef599139
--- /dev/null
+++ b/src/csharp/Grpc.Core/Internal/INativeCall.cs
@@ -0,0 +1,85 @@
+#region Copyright notice and license
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+
+namespace Grpc.Core.Internal
+{
+ internal delegate void UnaryResponseClientHandler(bool success, ClientSideStatus receivedStatus, byte[] receivedMessage, Metadata responseHeaders);
+
+ // Received status for streaming response calls.
+ internal delegate void ReceivedStatusOnClientHandler(bool success, ClientSideStatus receivedStatus);
+
+ internal delegate void ReceivedMessageHandler(bool success, byte[] receivedMessage);
+
+ internal delegate void ReceivedResponseHeadersHandler(bool success, Metadata responseHeaders);
+
+ internal delegate void SendCompletionHandler(bool success);
+
+ internal delegate void ReceivedCloseOnServerHandler(bool success, bool cancelled);
+
+ /// <summary>
+ /// Abstraction of a native call object.
+ /// </summary>
+ internal interface INativeCall : IDisposable
+ {
+ void Cancel();
+
+ void CancelWithStatus(Grpc.Core.Status status);
+
+ string GetPeer();
+
+ void StartUnary(UnaryResponseClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, Grpc.Core.WriteFlags writeFlags);
+
+ void StartUnary(BatchContextSafeHandle ctx, byte[] payload, MetadataArraySafeHandle metadataArray, Grpc.Core.WriteFlags writeFlags);
+
+ void StartClientStreaming(UnaryResponseClientHandler callback, MetadataArraySafeHandle metadataArray);
+
+ void StartServerStreaming(ReceivedStatusOnClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, Grpc.Core.WriteFlags writeFlags);
+
+ void StartDuplexStreaming(ReceivedStatusOnClientHandler callback, MetadataArraySafeHandle metadataArray);
+
+ void StartReceiveMessage(ReceivedMessageHandler callback);
+
+ void StartReceiveInitialMetadata(ReceivedResponseHeadersHandler callback);
+
+ void StartSendInitialMetadata(SendCompletionHandler callback, MetadataArraySafeHandle metadataArray);
+
+ void StartSendMessage(SendCompletionHandler callback, byte[] payload, Grpc.Core.WriteFlags writeFlags, bool sendEmptyInitialMetadata);
+
+ void StartSendCloseFromClient(SendCompletionHandler callback);
+
+ void StartSendStatusFromServer(SendCompletionHandler callback, Grpc.Core.Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata);
+
+ void StartServerSide(ReceivedCloseOnServerHandler callback);
+ }
+}
diff --git a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs
index 83994f6762..31b834c979 100644
--- a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs
@@ -35,7 +35,7 @@ using System.Threading.Tasks;
namespace Grpc.Core.Internal
{
/// <summary>
- /// grpc_metadata_array from <grpc/grpc.h>
+ /// grpc_metadata_array from <c>grpc/grpc.h</c>
/// </summary>
internal class MetadataArraySafeHandle : SafeHandleZeroIsInvalid
{
diff --git a/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs
index 37a4f5256b..51e352a18b 100644
--- a/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs
@@ -37,7 +37,7 @@ using Grpc.Core.Utils;
namespace Grpc.Core.Internal
{
/// <summary>
- /// grpc_server_credentials from <grpc/grpc_security.h>
+ /// grpc_server_credentials from <c>grpc/grpc_security.h</c>
/// </summary>
internal class ServerCredentialsSafeHandle : SafeHandleZeroIsInvalid
{
diff --git a/src/csharp/Grpc.Core/Marshaller.cs b/src/csharp/Grpc.Core/Marshaller.cs
index f38cb0863f..3493d2d38f 100644
--- a/src/csharp/Grpc.Core/Marshaller.cs
+++ b/src/csharp/Grpc.Core/Marshaller.cs
@@ -39,7 +39,7 @@ namespace Grpc.Core
/// <summary>
/// Encapsulates the logic for serializing and deserializing messages.
/// </summary>
- public struct Marshaller<T>
+ public class Marshaller<T>
{
readonly Func<T, byte[]> serializer;
readonly Func<byte[], T> deserializer;
diff --git a/src/csharp/Grpc.Core/Metadata.cs b/src/csharp/Grpc.Core/Metadata.cs
index a589b50caa..21bdf4f114 100644
--- a/src/csharp/Grpc.Core/Metadata.cs
+++ b/src/csharp/Grpc.Core/Metadata.cs
@@ -33,15 +33,23 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
+using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
+using System.Text.RegularExpressions;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
- /// Provides access to read and write metadata values to be exchanged during a call.
+ /// A collection of metadata entries that can be exchanged during a call.
+ /// gRPC supports these types of metadata:
+ /// <list type="bullet">
+ /// <item><term>Request headers</term><description>are sent by the client at the beginning of a remote call before any request messages are sent.</description></item>
+ /// <item><term>Response headers</term><description>are sent by the server at the beginning of a remote call handler before any response messages are sent.</description></item>
+ /// <item><term>Response trailers</term><description>are sent by the server at the end of a remote call along with resulting call status.</description></item>
+ /// </list>
/// </summary>
public sealed class Metadata : IList<Metadata.Entry>
{
@@ -58,21 +66,19 @@ namespace Grpc.Core
readonly List<Entry> entries;
bool readOnly;
+ /// <summary>
+ /// Initializes a new instance of <c>Metadata</c>.
+ /// </summary>
public Metadata()
{
this.entries = new List<Entry>();
}
- public Metadata(ICollection<Entry> entries)
- {
- this.entries = new List<Entry>(entries);
- }
-
/// <summary>
/// Makes this object read-only.
/// </summary>
/// <returns>this object</returns>
- public Metadata Freeze()
+ internal Metadata Freeze()
{
this.readOnly = true;
return this;
@@ -184,6 +190,7 @@ namespace Grpc.Core
public struct Entry
{
private static readonly Encoding Encoding = Encoding.ASCII;
+ private static readonly Regex ValidKeyRegex = new Regex("^[a-z0-9_-]+$");
readonly string key;
readonly string value;
@@ -197,7 +204,7 @@ namespace Grpc.Core
}
/// <summary>
- /// Initializes a new instance of the <see cref="Grpc.Core.Metadata+Entry"/> struct with a binary value.
+ /// Initializes a new instance of the <see cref="Grpc.Core.Metadata.Entry"/> struct with a binary value.
/// </summary>
/// <param name="key">Metadata key, needs to have suffix indicating a binary valued metadata entry.</param>
/// <param name="valueBytes">Value bytes.</param>
@@ -213,7 +220,7 @@ namespace Grpc.Core
}
/// <summary>
- /// Initializes a new instance of the <see cref="Grpc.Core.Metadata+Entry"/> struct holding an ASCII value.
+ /// Initializes a new instance of the <see cref="Grpc.Core.Metadata.Entry"/> struct holding an ASCII value.
/// </summary>
/// <param name="key">Metadata key, must not use suffix indicating a binary valued metadata entry.</param>
/// <param name="value">Value string. Only ASCII characters are allowed.</param>
@@ -280,7 +287,7 @@ namespace Grpc.Core
}
/// <summary>
- /// Returns a <see cref="System.String"/> that represents the current <see cref="Grpc.Core.Metadata+Entry"/>.
+ /// Returns a <see cref="System.String"/> that represents the current <see cref="Grpc.Core.Metadata.Entry"/>.
/// </summary>
public override string ToString()
{
@@ -316,7 +323,10 @@ namespace Grpc.Core
private static string NormalizeKey(string key)
{
- return Preconditions.CheckNotNull(key, "key").ToLower();
+ var normalized = Preconditions.CheckNotNull(key, "key").ToLower(CultureInfo.InvariantCulture);
+ Preconditions.CheckArgument(ValidKeyRegex.IsMatch(normalized),
+ "Metadata entry key not valid. Keys can only contain lowercase alphanumeric characters, underscores and hyphens.");
+ return normalized;
}
}
}
diff --git a/src/csharp/Grpc.Core/Method.cs b/src/csharp/Grpc.Core/Method.cs
index 4c53285893..99162a7d5d 100644
--- a/src/csharp/Grpc.Core/Method.cs
+++ b/src/csharp/Grpc.Core/Method.cs
@@ -84,6 +84,8 @@ namespace Grpc.Core
/// <summary>
/// A description of a remote method.
/// </summary>
+ /// <typeparam name="TRequest">Request message type for this method.</typeparam>
+ /// <typeparam name="TResponse">Response message type for this method.</typeparam>
public class Method<TRequest, TResponse> : IMethod
{
readonly MethodType type;
diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs
index 28f1686e20..7c94d21561 100644
--- a/src/csharp/Grpc.Core/Server.cs
+++ b/src/csharp/Grpc.Core/Server.cs
@@ -44,7 +44,7 @@ using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
- /// A gRPC server.
+ /// gRPC server. A single server can server arbitrary number of services and can listen on more than one ports.
/// </summary>
public class Server
{
@@ -324,6 +324,9 @@ namespace Grpc.Core
server.AddServiceDefinitionInternal(serviceDefinition);
}
+ /// <summary>
+ /// Gets enumerator for this collection.
+ /// </summary>
public IEnumerator<ServerServiceDefinition> GetEnumerator()
{
return server.serviceDefinitionsList.GetEnumerator();
@@ -369,6 +372,9 @@ namespace Grpc.Core
return Add(new ServerPort(host, port, credentials));
}
+ /// <summary>
+ /// Gets enumerator for this collection.
+ /// </summary>
public IEnumerator<ServerPort> GetEnumerator()
{
return server.serverPortList.GetEnumerator();
diff --git a/src/csharp/Grpc.Core/ServerCallContext.cs b/src/csharp/Grpc.Core/ServerCallContext.cs
index 75d81c64f3..09a6b882a6 100644
--- a/src/csharp/Grpc.Core/ServerCallContext.cs
+++ b/src/csharp/Grpc.Core/ServerCallContext.cs
@@ -72,6 +72,13 @@ namespace Grpc.Core
this.writeOptionsHolder = writeOptionsHolder;
}
+ /// <summary>
+ /// Asynchronously sends response headers for the current call to the client. This method may only be invoked once for each call and needs to be invoked
+ /// before any response messages are written. Writing the first response message implicitly sends empty response headers if <c>WriteResponseHeadersAsync</c> haven't
+ /// been called yet.
+ /// </summary>
+ /// <param name="responseHeaders">The response headers to send.</param>
+ /// <returns>The task that finished once response headers have been written.</returns>
public Task WriteResponseHeadersAsync(Metadata responseHeaders)
{
return writeHeadersFunc(responseHeaders);
@@ -186,6 +193,9 @@ namespace Grpc.Core
/// </summary>
public interface IHasWriteOptions
{
+ /// <summary>
+ /// Gets or sets the write options.
+ /// </summary>
WriteOptions WriteOptions { get; set; }
}
}
diff --git a/src/csharp/Grpc.Core/ServerMethods.cs b/src/csharp/Grpc.Core/ServerMethods.cs
index 1f119a80ff..728f77cde5 100644
--- a/src/csharp/Grpc.Core/ServerMethods.cs
+++ b/src/csharp/Grpc.Core/ServerMethods.cs
@@ -38,6 +38,8 @@ namespace Grpc.Core
/// <summary>
/// Server-side handler for unary call.
/// </summary>
+ /// <typeparam name="TRequest">Request message type for this method.</typeparam>
+ /// <typeparam name="TResponse">Response message type for this method.</typeparam>
public delegate Task<TResponse> UnaryServerMethod<TRequest, TResponse>(TRequest request, ServerCallContext context)
where TRequest : class
where TResponse : class;
@@ -45,6 +47,8 @@ namespace Grpc.Core
/// <summary>
/// Server-side handler for client streaming call.
/// </summary>
+ /// <typeparam name="TRequest">Request message type for this method.</typeparam>
+ /// <typeparam name="TResponse">Response message type for this method.</typeparam>
public delegate Task<TResponse> ClientStreamingServerMethod<TRequest, TResponse>(IAsyncStreamReader<TRequest> requestStream, ServerCallContext context)
where TRequest : class
where TResponse : class;
@@ -52,6 +56,8 @@ namespace Grpc.Core
/// <summary>
/// Server-side handler for server streaming call.
/// </summary>
+ /// <typeparam name="TRequest">Request message type for this method.</typeparam>
+ /// <typeparam name="TResponse">Response message type for this method.</typeparam>
public delegate Task ServerStreamingServerMethod<TRequest, TResponse>(TRequest request, IServerStreamWriter<TResponse> responseStream, ServerCallContext context)
where TRequest : class
where TResponse : class;
@@ -59,6 +65,8 @@ namespace Grpc.Core
/// <summary>
/// Server-side handler for bidi streaming call.
/// </summary>
+ /// <typeparam name="TRequest">Request message type for this method.</typeparam>
+ /// <typeparam name="TResponse">Response message type for this method.</typeparam>
public delegate Task DuplexStreamingServerMethod<TRequest, TResponse>(IAsyncStreamReader<TRequest> requestStream, IServerStreamWriter<TResponse> responseStream, ServerCallContext context)
where TRequest : class
where TResponse : class;
diff --git a/src/csharp/Grpc.Core/ServerServiceDefinition.cs b/src/csharp/Grpc.Core/ServerServiceDefinition.cs
index 94b0a320c3..deb1431ca3 100644
--- a/src/csharp/Grpc.Core/ServerServiceDefinition.cs
+++ b/src/csharp/Grpc.Core/ServerServiceDefinition.cs
@@ -40,6 +40,8 @@ namespace Grpc.Core
{
/// <summary>
/// Mapping of method names to server call handlers.
+ /// Normally, the <c>ServerServiceDefinition</c> objects will be created by the <c>BindService</c> factory method
+ /// that is part of the autogenerated code for a protocol buffers service definition.
/// </summary>
public class ServerServiceDefinition
{
@@ -58,21 +60,41 @@ namespace Grpc.Core
}
}
+ /// <summary>
+ /// Creates a new builder object for <c>ServerServiceDefinition</c>.
+ /// </summary>
+ /// <param name="serviceName">The service name.</param>
+ /// <returns>The builder object.</returns>
public static Builder CreateBuilder(string serviceName)
{
return new Builder(serviceName);
}
+ /// <summary>
+ /// Builder class for <see cref="ServerServiceDefinition"/>.
+ /// </summary>
public class Builder
{
readonly string serviceName;
readonly Dictionary<string, IServerCallHandler> callHandlers = new Dictionary<string, IServerCallHandler>();
+ /// <summary>
+ /// Creates a new instance of builder.
+ /// </summary>
+ /// <param name="serviceName">The service name.</param>
public Builder(string serviceName)
{
this.serviceName = serviceName;
}
+ /// <summary>
+ /// Adds a definitions for a single request - single response method.
+ /// </summary>
+ /// <typeparam name="TRequest">The request message class.</typeparam>
+ /// <typeparam name="TResponse">The response message class.</typeparam>
+ /// <param name="method">The method.</param>
+ /// <param name="handler">The method handler.</param>
+ /// <returns>This builder instance.</returns>
public Builder AddMethod<TRequest, TResponse>(
Method<TRequest, TResponse> method,
UnaryServerMethod<TRequest, TResponse> handler)
@@ -83,6 +105,14 @@ namespace Grpc.Core
return this;
}
+ /// <summary>
+ /// Adds a definitions for a client streaming method.
+ /// </summary>
+ /// <typeparam name="TRequest">The request message class.</typeparam>
+ /// <typeparam name="TResponse">The response message class.</typeparam>
+ /// <param name="method">The method.</param>
+ /// <param name="handler">The method handler.</param>
+ /// <returns>This builder instance.</returns>
public Builder AddMethod<TRequest, TResponse>(
Method<TRequest, TResponse> method,
ClientStreamingServerMethod<TRequest, TResponse> handler)
@@ -93,6 +123,14 @@ namespace Grpc.Core
return this;
}
+ /// <summary>
+ /// Adds a definitions for a server streaming method.
+ /// </summary>
+ /// <typeparam name="TRequest">The request message class.</typeparam>
+ /// <typeparam name="TResponse">The response message class.</typeparam>
+ /// <param name="method">The method.</param>
+ /// <param name="handler">The method handler.</param>
+ /// <returns>This builder instance.</returns>
public Builder AddMethod<TRequest, TResponse>(
Method<TRequest, TResponse> method,
ServerStreamingServerMethod<TRequest, TResponse> handler)
@@ -103,6 +141,14 @@ namespace Grpc.Core
return this;
}
+ /// <summary>
+ /// Adds a definitions for a bidirectional streaming method.
+ /// </summary>
+ /// <typeparam name="TRequest">The request message class.</typeparam>
+ /// <typeparam name="TResponse">The response message class.</typeparam>
+ /// <param name="method">The method.</param>
+ /// <param name="handler">The method handler.</param>
+ /// <returns>This builder instance.</returns>
public Builder AddMethod<TRequest, TResponse>(
Method<TRequest, TResponse> method,
DuplexStreamingServerMethod<TRequest, TResponse> handler)
@@ -113,6 +159,10 @@ namespace Grpc.Core
return this;
}
+ /// <summary>
+ /// Creates an immutable <c>ServerServiceDefinition</c> from this builder.
+ /// </summary>
+ /// <returns>The <c>ServerServiceDefinition</c> object.</returns>
public ServerServiceDefinition Build()
{
return new ServerServiceDefinition(callHandlers);
diff --git a/src/csharp/Grpc.Core/Utils/Preconditions.cs b/src/csharp/Grpc.Core/Utils/Preconditions.cs
index 374262f87a..a8ab603391 100644
--- a/src/csharp/Grpc.Core/Utils/Preconditions.cs
+++ b/src/csharp/Grpc.Core/Utils/Preconditions.cs
@@ -43,6 +43,7 @@ namespace Grpc.Core.Utils
/// <summary>
/// Throws <see cref="ArgumentException"/> if condition is false.
/// </summary>
+ /// <param name="condition">The condition.</param>
public static void CheckArgument(bool condition)
{
if (!condition)
@@ -54,6 +55,8 @@ namespace Grpc.Core.Utils
/// <summary>
/// Throws <see cref="ArgumentException"/> with given message if condition is false.
/// </summary>
+ /// <param name="condition">The condition.</param>
+ /// <param name="errorMessage">The error message.</param>
public static void CheckArgument(bool condition, string errorMessage)
{
if (!condition)
@@ -65,6 +68,7 @@ namespace Grpc.Core.Utils
/// <summary>
/// Throws <see cref="ArgumentNullException"/> if reference is null.
/// </summary>
+ /// <param name="reference">The reference.</param>
public static T CheckNotNull<T>(T reference)
{
if (reference == null)
@@ -77,6 +81,8 @@ namespace Grpc.Core.Utils
/// <summary>
/// Throws <see cref="ArgumentNullException"/> if reference is null.
/// </summary>
+ /// <param name="reference">The reference.</param>
+ /// <param name="paramName">The parameter name.</param>
public static T CheckNotNull<T>(T reference, string paramName)
{
if (reference == null)
@@ -89,6 +95,7 @@ namespace Grpc.Core.Utils
/// <summary>
/// Throws <see cref="InvalidOperationException"/> if condition is false.
/// </summary>
+ /// <param name="condition">The condition.</param>
public static void CheckState(bool condition)
{
if (!condition)
@@ -100,6 +107,8 @@ namespace Grpc.Core.Utils
/// <summary>
/// Throws <see cref="InvalidOperationException"/> with given message if condition is false.
/// </summary>
+ /// <param name="condition">The condition.</param>
+ /// <param name="errorMessage">The error message.</param>
public static void CheckState(bool condition, string errorMessage)
{
if (!condition)
diff --git a/src/csharp/Grpc.Core/VersionInfo.cs b/src/csharp/Grpc.Core/VersionInfo.cs
index b6dbd3b49c..eda821bc31 100644
--- a/src/csharp/Grpc.Core/VersionInfo.cs
+++ b/src/csharp/Grpc.Core/VersionInfo.cs
@@ -39,8 +39,8 @@ namespace Grpc.Core
public static class VersionInfo
{
/// <summary>
- /// Current version of gRPC
+ /// Current version of gRPC C#
/// </summary>
- public const string CurrentVersion = "0.6.1";
+ public const string CurrentVersion = "0.7.0";
}
}
diff --git a/src/csharp/Grpc.Core/WriteOptions.cs b/src/csharp/Grpc.Core/WriteOptions.cs
index 7ef3189d76..7523ada84a 100644
--- a/src/csharp/Grpc.Core/WriteOptions.cs
+++ b/src/csharp/Grpc.Core/WriteOptions.cs
@@ -66,11 +66,18 @@ namespace Grpc.Core
private WriteFlags flags;
+ /// <summary>
+ /// Initializes a new instance of <c>WriteOptions</c> class.
+ /// </summary>
+ /// <param name="flags">The write flags.</param>
public WriteOptions(WriteFlags flags = default(WriteFlags))
{
this.flags = flags;
}
+ /// <summary>
+ /// Gets the write flags.
+ /// </summary>
public WriteFlags Flags
{
get