diff options
author | Jan Tattermusch <jtattermusch@users.noreply.github.com> | 2016-05-10 20:43:05 -0700 |
---|---|---|
committer | Jan Tattermusch <jtattermusch@users.noreply.github.com> | 2016-05-10 20:43:05 -0700 |
commit | 1fc79fccf4f78e39032dc51d2fba26ba0f706bef (patch) | |
tree | e4facd23fe989a9cbb6099b387f00fc42a763fc5 /src/csharp | |
parent | d6dabf9e9d6d858b552726bafb3cb69a347ed13f (diff) | |
parent | a46d21de37c60554dd455104dbbec37854515253 (diff) |
Merge pull request #6493 from jtattermusch/csharp_streaming_api_exceptions
C#: make writes throw RpcException if the client-side call has already finished
Diffstat (limited to 'src/csharp')
-rw-r--r-- | src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs | 51 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Internal/AsyncCall.cs | 15 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Internal/AsyncCallBase.cs | 2 | ||||
-rw-r--r-- | src/csharp/Grpc.IntegrationTesting/InteropClient.cs | 4 |
4 files changed, 64 insertions, 8 deletions
diff --git a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs index abe9d4a2e6..777a1c8c50 100644 --- a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs +++ b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs @@ -181,13 +181,14 @@ namespace Grpc.Core.Internal.Tests } [Test] - public void ClientStreaming_WriteFailure() + public void ClientStreaming_WriteCompletionFailure() { var resultTask = asyncCall.ClientStreamingCallAsync(); var requestStream = new ClientRequestStream<string, string>(asyncCall); var writeTask = requestStream.WriteAsync("request1"); fakeCall.SendCompletionHandler(false); + // TODO: maybe IOException or waiting for RPCException is more appropriate here. Assert.ThrowsAsync(typeof(InvalidOperationException), async () => await writeTask); fakeCall.UnaryResponseClientHandler(true, @@ -199,7 +200,7 @@ namespace Grpc.Core.Internal.Tests } [Test] - public void ClientStreaming_WriteAfterReceivingStatusFails() + public void ClientStreaming_WriteAfterReceivingStatusThrowsRpcException() { var resultTask = asyncCall.ClientStreamingCallAsync(); var requestStream = new ClientRequestStream<string, string>(asyncCall); @@ -210,7 +211,44 @@ namespace Grpc.Core.Internal.Tests new Metadata()); AssertUnaryResponseSuccess(asyncCall, fakeCall, resultTask); + var ex = Assert.Throws<RpcException>(() => requestStream.WriteAsync("request1")); + Assert.AreEqual(Status.DefaultSuccess, ex.Status); + } + + [Test] + public void ClientStreaming_WriteAfterReceivingStatusThrowsRpcException2() + { + var resultTask = asyncCall.ClientStreamingCallAsync(); + var requestStream = new ClientRequestStream<string, string>(asyncCall); + + fakeCall.UnaryResponseClientHandler(true, + new ClientSideStatus(new Status(StatusCode.OutOfRange, ""), new Metadata()), + CreateResponsePayload(), + new Metadata()); + + AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.OutOfRange); + var ex = Assert.Throws<RpcException>(() => requestStream.WriteAsync("request1")); + Assert.AreEqual(StatusCode.OutOfRange, ex.Status.StatusCode); + } + + [Test] + public void ClientStreaming_WriteAfterCompleteThrowsInvalidOperationException() + { + var resultTask = asyncCall.ClientStreamingCallAsync(); + var requestStream = new ClientRequestStream<string, string>(asyncCall); + + requestStream.CompleteAsync(); + Assert.Throws(typeof(InvalidOperationException), () => requestStream.WriteAsync("request1")); + + fakeCall.SendCompletionHandler(true); + + fakeCall.UnaryResponseClientHandler(true, + new ClientSideStatus(Status.DefaultSuccess, new Metadata()), + CreateResponsePayload(), + new Metadata()); + + AssertUnaryResponseSuccess(asyncCall, fakeCall, resultTask); } [Test] @@ -229,7 +267,7 @@ namespace Grpc.Core.Internal.Tests } [Test] - public void ClientStreaming_WriteAfterCancellationRequestFails() + public void ClientStreaming_WriteAfterCancellationRequestThrowsOperationCancelledException() { var resultTask = asyncCall.ClientStreamingCallAsync(); var requestStream = new ClientRequestStream<string, string>(asyncCall); @@ -340,7 +378,7 @@ namespace Grpc.Core.Internal.Tests } [Test] - public void DuplexStreaming_WriteAfterReceivingStatusFails() + public void DuplexStreaming_WriteAfterReceivingStatusThrowsRpcException() { asyncCall.StartDuplexStreamingCall(); var requestStream = new ClientRequestStream<string, string>(asyncCall); @@ -352,7 +390,8 @@ namespace Grpc.Core.Internal.Tests AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask); - Assert.ThrowsAsync(typeof(InvalidOperationException), async () => await requestStream.WriteAsync("request1")); + var ex = Assert.ThrowsAsync<RpcException>(async () => await requestStream.WriteAsync("request1")); + Assert.AreEqual(Status.DefaultSuccess, ex.Status); } [Test] @@ -372,7 +411,7 @@ namespace Grpc.Core.Internal.Tests } [Test] - public void DuplexStreaming_WriteAfterCancellationRequestFails() + public void DuplexStreaming_WriteAfterCancellationRequestThrowsOperationCancelledException() { asyncCall.StartDuplexStreamingCall(); var requestStream = new ClientRequestStream<string, string>(asyncCall); diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs index f522174bd0..55351869b5 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs @@ -57,7 +57,7 @@ namespace Grpc.Core.Internal // Completion of a pending unary response if not null. TaskCompletionSource<TResponse> unaryResponseTcs; - // Indicates that steaming call has finished. + // Indicates that response streaming call has finished. TaskCompletionSource<object> streamingCallFinishedTcs = new TaskCompletionSource<object>(); // Response headers set here once received. @@ -443,6 +443,19 @@ namespace Grpc.Core.Internal } } + protected override void CheckSendingAllowed(bool allowFinished) + { + base.CheckSendingAllowed(true); + + // throwing RpcException if we already received status on client + // side makes the most sense. + // Note that this throws even for StatusCode.OK. + if (!allowFinished && finishedStatus.HasValue) + { + throw new RpcException(finishedStatus.Value.Status); + } + } + /// <summary> /// Handles receive status completion for calls with streaming response. /// </summary> diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs index 42234dcac2..4de23706b2 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs @@ -213,7 +213,7 @@ namespace Grpc.Core.Internal { } - protected void CheckSendingAllowed(bool allowFinished) + protected virtual void CheckSendingAllowed(bool allowFinished) { GrpcPreconditions.CheckState(started); CheckNotCancelled(); diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs index b3b1abf1bc..cff8508631 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs @@ -492,6 +492,10 @@ namespace Grpc.IntegrationTesting { // Deadline was reached before write has started. Eat the exception and continue. } + catch (RpcException) + { + // Deadline was reached before write has started. Eat the exception and continue. + } var ex = Assert.ThrowsAsync<RpcException>(async () => await call.ResponseStream.MoveNext()); // We can't guarantee the status code always DeadlineExceeded. See issue #2685. |