From e64825c302e1eacac43d2b21049fddaa25e11902 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Tue, 8 Aug 2017 16:10:23 +0200 Subject: Populate trailers in RpcException --- src/csharp/Grpc.Core/Internal/AsyncCall.cs | 8 ++++---- src/csharp/Grpc.Core/RpcException.cs | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) (limited to 'src/csharp/Grpc.Core') 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(); - 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/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; /// /// Creates a new RpcException 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; } /// @@ -44,6 +47,18 @@ namespace Grpc.Core public RpcException(Status status, string message) : base(message) { this.status = status; + this.trailers = Metadata.Empty; + } + + /// + /// Creates a new RpcException associated with given status and trailing response metadata. + /// + /// Resulting status of a call. + /// Response trailing metadata. + public RpcException(Status status, Metadata trailers) : base(status.ToString()) + { + this.status = status; + this.trailers = GrpcPreconditions.CheckNotNull(trailers); } /// @@ -56,5 +71,18 @@ namespace Grpc.Core return status; } } + + /// + /// 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 RpcException thrown by the server-side part of the stack will have trailers always set to empty. + /// + public Metadata Trailers + { + get + { + return trailers; + } + } } } -- cgit v1.2.3 From 2565e732f79da9392cc8656229ea97dd045fbb4f Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Tue, 8 Aug 2017 16:53:31 +0200 Subject: merging of trailers on server-side --- src/csharp/Grpc.Core/Internal/ServerCallHandler.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'src/csharp/Grpc.Core') diff --git a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs index 36702a3fab..638a8d8a1f 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.StatusFromException(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.StatusFromException(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.StatusFromException(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.StatusFromException(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 StatusFromException(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; } -- cgit v1.2.3 From 9dc182161dfb35ed8f7bb45150bb53e097fcebe1 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Wed, 9 Aug 2017 19:36:50 +0200 Subject: better name for StatusFromException --- src/csharp/Grpc.Core/Internal/ServerCallHandler.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/csharp/Grpc.Core') diff --git a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs index 638a8d8a1f..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, context.ResponseTrailers); + 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, context.ResponseTrailers); + 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, context.ResponseTrailers); + 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, context.ResponseTrailers); + status = HandlerUtils.GetStatusFromExceptionAndMergeTrailers(e, context.ResponseTrailers); } try { @@ -292,7 +292,7 @@ namespace Grpc.Core.Internal internal static class HandlerUtils { - public static Status StatusFromException(Exception e, Metadata callContextResponseTrailers) + public static Status GetStatusFromExceptionAndMergeTrailers(Exception e, Metadata callContextResponseTrailers) { var rpcException = e as RpcException; if (rpcException != null) -- cgit v1.2.3