diff options
author | Jan Tattermusch <jtattermusch@users.noreply.github.com> | 2017-08-10 11:23:59 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-10 11:23:59 +0200 |
commit | a48f879aef6d33b69b544ed971c9f45b9cae3658 (patch) | |
tree | fc73f29cccac0064834d4c0bc0552cf06ddb9d0d /src/csharp/Grpc.Core | |
parent | 0fcd99e1682b15699af43b4ed4390e8be78aafb0 (diff) | |
parent | 9dc182161dfb35ed8f7bb45150bb53e097fcebe1 (diff) |
Merge pull request #12120 from jtattermusch/csharp_rpcexception
Add Trailers property to RpcException
Diffstat (limited to 'src/csharp/Grpc.Core')
-rw-r--r-- | src/csharp/Grpc.Core/Internal/AsyncCall.cs | 8 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Internal/ServerCallHandler.cs | 19 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/RpcException.cs | 28 |
3 files changed, 46 insertions, 9 deletions
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs index 6e6ca7cd53..17109de587 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs @@ -329,7 +329,7 @@ namespace Grpc.Core.Internal protected override Exception GetRpcExceptionClientOnly() { - return new RpcException(finishedStatus.Value.Status); + return new RpcException(finishedStatus.Value.Status, finishedStatus.Value.Trailers); } protected override Task CheckSendAllowedOrEarlyResult() @@ -348,7 +348,7 @@ namespace Grpc.Core.Internal // Writing after the call has finished is not a programming error because server can close // the call anytime, so don't throw directly, but let the write task finish with an error. var tcs = new TaskCompletionSource<object>(); - tcs.SetException(new RpcException(finishedStatus.Value.Status)); + tcs.SetException(new RpcException(finishedStatus.Value.Status, finishedStatus.Value.Trailers)); return tcs.Task; } @@ -468,7 +468,7 @@ namespace Grpc.Core.Internal var status = receivedStatus.Status; if (status.StatusCode != StatusCode.OK) { - unaryResponseTcs.SetException(new RpcException(status)); + unaryResponseTcs.SetException(new RpcException(status, receivedStatus.Trailers)); return; } @@ -506,7 +506,7 @@ namespace Grpc.Core.Internal var status = receivedStatus.Status; if (status.StatusCode != StatusCode.OK) { - streamingResponseCallFinishedTcs.SetException(new RpcException(status)); + streamingResponseCallFinishedTcs.SetException(new RpcException(status, receivedStatus.Trailers)); return; } diff --git a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs index 36702a3fab..6019f8e793 100644 --- a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs +++ b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs @@ -76,7 +76,7 @@ namespace Grpc.Core.Internal { Logger.Warning(e, "Exception occured in handler."); } - status = HandlerUtils.StatusFromException(e); + status = HandlerUtils.GetStatusFromExceptionAndMergeTrailers(e, context.ResponseTrailers); } try { @@ -133,7 +133,7 @@ namespace Grpc.Core.Internal { Logger.Warning(e, "Exception occured in handler."); } - status = HandlerUtils.StatusFromException(e); + status = HandlerUtils.GetStatusFromExceptionAndMergeTrailers(e, context.ResponseTrailers); } try @@ -191,7 +191,7 @@ namespace Grpc.Core.Internal { Logger.Warning(e, "Exception occured in handler."); } - status = HandlerUtils.StatusFromException(e); + status = HandlerUtils.GetStatusFromExceptionAndMergeTrailers(e, context.ResponseTrailers); } try @@ -247,7 +247,7 @@ namespace Grpc.Core.Internal { Logger.Warning(e, "Exception occured in handler."); } - status = HandlerUtils.StatusFromException(e); + status = HandlerUtils.GetStatusFromExceptionAndMergeTrailers(e, context.ResponseTrailers); } try { @@ -292,11 +292,20 @@ namespace Grpc.Core.Internal internal static class HandlerUtils { - public static Status StatusFromException(Exception e) + public static Status GetStatusFromExceptionAndMergeTrailers(Exception e, Metadata callContextResponseTrailers) { var rpcException = e as RpcException; if (rpcException != null) { + // There are two sources of metadata entries on the server-side: + // 1. serverCallContext.ResponseTrailers + // 2. trailers in RpcException thrown by user code in server side handler. + // As metadata allows duplicate keys, the logical thing to do is + // to just merge trailers from RpcException into serverCallContext.ResponseTrailers. + foreach (var entry in rpcException.Trailers) + { + callContextResponseTrailers.Add(entry); + } // use the status thrown by handler. return rpcException.Status; } diff --git a/src/csharp/Grpc.Core/RpcException.cs b/src/csharp/Grpc.Core/RpcException.cs index 01b9e4fb1a..d2c912e73a 100644 --- a/src/csharp/Grpc.Core/RpcException.cs +++ b/src/csharp/Grpc.Core/RpcException.cs @@ -17,6 +17,7 @@ #endregion using System; +using Grpc.Core.Utils; namespace Grpc.Core { @@ -26,6 +27,7 @@ namespace Grpc.Core public class RpcException : Exception { private readonly Status status; + private readonly Metadata trailers; /// <summary> /// Creates a new <c>RpcException</c> associated with given status. @@ -34,6 +36,7 @@ namespace Grpc.Core public RpcException(Status status) : base(status.ToString()) { this.status = status; + this.trailers = Metadata.Empty; } /// <summary> @@ -44,6 +47,18 @@ namespace Grpc.Core public RpcException(Status status, string message) : base(message) { this.status = status; + this.trailers = Metadata.Empty; + } + + /// <summary> + /// Creates a new <c>RpcException</c> associated with given status and trailing response metadata. + /// </summary> + /// <param name="status">Resulting status of a call.</param> + /// <param name="trailers">Response trailing metadata.</param> + public RpcException(Status status, Metadata trailers) : base(status.ToString()) + { + this.status = status; + this.trailers = GrpcPreconditions.CheckNotNull(trailers); } /// <summary> @@ -56,5 +71,18 @@ namespace Grpc.Core return status; } } + + /// <summary> + /// Gets the call trailing metadata. + /// Trailers only have meaningful content for client-side calls (in which case they represent the trailing metadata sent by the server when closing the call). + /// Instances of <c>RpcException</c> thrown by the server-side part of the stack will have trailers always set to empty. + /// </summary> + public Metadata Trailers + { + get + { + return trailers; + } + } } } |